P5561 题解
每个子串出现的时间都是一段区间,考虑线段树分治。
线段树的每个节点动态维护当前
那么瓶颈变为求任意两个子串的
先把所有串接在一起建反串 SAM。
一个观察是任意子串
但是本题过于卡空间,要把欧拉序求 LCA 换成树剖求 LCA。虽然变成了
#include<bits/stdc++.h>
#define il inline
using namespace std;
const int maxn=2000010;
const int MAXN=4000010;
il int read(){
int x=0;
char c=getchar();
for(;!(c>='0'&&c<='9');c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+c-'0';
return x;
}
struct edge{
int v,to;
}e[maxn<<1];
int head[maxn],ecnt;
void addedge(int u,int v){
e[++ecnt].v=v,e[ecnt].to=head[u],head[u]=ecnt;
}
struct sam{
int ch[26],fa,len;
}a[maxn];
int loc[maxn],cnt=1,End=1;
void Insert(int c){
int t=End,x=End=++cnt,nt=0,nx; a[x].len=a[t].len+1;
loc[a[x].len]=x;//////////////////
for(;t&&!nt;t=a[t].fa,nt=a[t].ch[c]) a[t].ch[c]=x;
if(!t){a[x].fa=1;return ;}
if(a[t].len+1==a[nt].len){a[x].fa=nt;return ;}
nx=++cnt,a[nx].len=a[t].len+1;
a[nx].fa=a[nt].fa,a[nt].fa=a[x].fa=nx;
for(int i=0;i<26;i++) a[nx].ch[i]=a[nt].ch[i];
for(;a[t].ch[c]==nt;t=a[t].fa) a[t].ch[c]=nx;
}
struct str{
int l,r,tl,tr;
}A[maxn];
int sz[maxn],zson[maxn],dep[maxn],fa[maxn];
int dfn[maxn],DFN[maxn],top[maxn],idx;
void dfs1(int fath,int x){
// printf("!!!%d\n",x);
dep[x]=dep[fath]+1,fa[x]=fath;
for(int i=head[x];i;i=e[i].to)
if(e[i].v^fath){
dfs1(x,e[i].v),sz[x]+=sz[e[i].v];
if(sz[zson[x]]<sz[e[i].v]) zson[x]=e[i].v;
}sz[x]++;
}
void dfs2(int x){
dfn[x]=++idx,DFN[idx]=x;
if(zson[fa[x]]^x) top[x]=x;
else top[x]=top[fa[x]];
if(zson[x]) dfs2(zson[x]);
for(int i=head[x];i;i=e[i].to)
if(e[i].v!=fa[x]&&e[i].v!=zson[x]) dfs2(e[i].v);
}
int dl[MAXN],dr[MAXN];
int n,m,q,L[maxn],R[maxn];
char c[maxn],C[maxn];
int lca(int x,int y){
while(top[x]^top[y])
dfn[top[x]]>dfn[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dfn[x]>dfn[y]?y:x;
}
int lcp(int i,int j){
return a[lca(loc[n-i+1],loc[n-j+1])].len;
}
void Add(int i,int l,int r,int L,int R,int sl,int sr){
if(l>=L&&r<=R){
if(dl[i]&&dr[i]) dr[i]=dl[i]+min(lcp(dl[i],sl),min(dr[i]-dl[i]+1,sr-sl+1))-1;
else dl[i]=sl,dr[i]=sr;
return ;
}int mid=l+r>>1;
if(mid>=L) Add(i<<1,l,mid,L,R,sl,sr);
if(mid<R) Add(i<<1|1,mid+1,r,L,R,sl,sr);
}
void Print(int i,int l,int r,int sl,int sr){
if(dl[i]&&dr[i]){
if(sl&&sr) sr=sl+min(lcp(dl[i],sl),min(dr[i]-dl[i]+1,sr-sl+1))-1;
else sl=dl[i],sr=dr[i];
}
if(l==r){
printf("%d\n",(sl&&sr)?sr-sl+1:0);
return ;
}int mid=l+r>>1;
Print(i<<1,l,mid,sl,sr);
Print(i<<1|1,mid+1,r,sl,sr);
}
int main(){
m=read();
for(int i=1;i<=m;i++){
scanf("%s",C+1);
L[i]=R[i-1]+1,R[i]=L[i]+strlen(C+1)-1;
for(int j=L[i];j<=R[i];j++) c[j]=C[j-L[i]+1];
}n=strlen(c+1);
for(int i=n;i;i--) Insert(c[i]-'a');
for(int i=2;i<=cnt;i++)
addedge(a[i].fa,i),addedge(i,a[i].fa);
dfs1(0,1),dfs2(1);
q=read();
for(int i=1;i<=q;i++){
int op=read(),x=read();
if(op==1){
A[i].l=L[x]+read()-1;
A[i].r=L[x]+read()-1;
A[i].tl=i;
}
else A[x].tr=i-1;
}
for(int i=1;i<=q;i++)
if(!A[i].tr) A[i].tr=q;
for(int i=1;i<=q;i++)
if(A[i].l) Add(1,1,q,A[i].tl,A[i].tr,A[i].l,A[i].r);
Print(1,1,q,0,0);
return 0;
}