bzoj 4556 字符串
题目大意:
一个字符串 $m$次询问
每次询问$s[a:b]$的所有子串与$s[c:d]$的$lcp$最大值
思路:
因为有$lcp$ 考虑把串翻转转化为$lcs$
建立后缀树,如何在树上通过$s[c:d]$查找是否存在$s[a:b]$的一个子串
可以二分答案$x$,找到树上$d$点的最远的一个祖先满足其$maxlen \ge x$,通过倍增实现
在这个节点的所有$right$中若能找到$a+x-1$至$b$中的一个,则说明该答案可行
实现$right$集合可以使用线段树合并,因为是儿子的后缀所以可以继承
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 #include<map> 10 #include<set> 11 #define ll long long 12 #define inf 2139062143 13 #define MAXN 200100 14 #define MOD 998244353 15 #define rep(i,s,t) for(register int i=(s),i##__end=(t);i<=i##__end;++i) 16 #define dwn(i,s,t) for(register int i=(s),i##__end=(t);i>=i##__end;--i) 17 #define ren for(register int i=fst[x];i;i=nxt[i]) 18 #define pb(i,x) vec[i].push_back(x) 19 #define pls(a,b) (a+b)%MOD 20 #define mns(a,b) (a-b+MOD)%MOD 21 #define mul(a,b) (1LL*(a)*(b))%MOD 22 using namespace std; 23 inline int read() 24 { 25 int x=0,f=1;char ch=getchar(); 26 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 27 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 28 return x*f; 29 } 30 int n,m,tot,las,tr[MAXN][26],mxl[MAXN],fa[MAXN],pos[MAXN]; 31 char ch[MAXN]; 32 int fst[MAXN],nxt[MAXN],to[MAXN],cnt,f[MAXN][20],dep[MAXN]; 33 int rt[MAXN],num,ls[MAXN*20],rs[MAXN*20]; 34 void add(int u,int v) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;} 35 int merge(int x,int y) 36 { 37 if(!(x*y)) return x|y;int z=++num; 38 ls[z]=merge(ls[x],ls[y]),rs[z]=merge(rs[x],rs[y]); 39 return z; 40 } 41 void mdf(int &k,int l,int r,int x) 42 { 43 if(!k) k=++num;if(l==r) return ;int mid=l+r>>1; 44 if(x<=mid) mdf(ls[k],l,mid,x); 45 if(x>mid) mdf(rs[k],mid+1,r,x); 46 } 47 int query(int k,int l,int r,int a,int b) 48 { 49 if(!k) return 0;if(l==a&&r==b) return 1;int mid=l+r>>1; 50 if(b<=mid) return query(ls[k],l,mid,a,b); 51 else if(a>mid) return query(rs[k],mid+1,r,a,b); 52 else return query(ls[k],l,mid,a,mid)|query(rs[k],mid+1,r,mid+1,b); 53 } 54 void extend(int c,int x) 55 { 56 int p=las,np=las=++tot;mxl[np]=mxl[p]+1;mdf(rt[np],1,n,x); 57 for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=np; 58 if(!p) {fa[np]=1;return ;} 59 int q=tr[p][c];if(mxl[q]==mxl[p]+1) {fa[np]=q;return ;} 60 int nq=++tot;mxl[nq]=mxl[p]+1; 61 memcpy(tr[nq],tr[q],sizeof(tr[nq])); 62 fa[nq]=fa[q],fa[np]=fa[q]=nq; 63 for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq; 64 } 65 void dfs(int x,int pa) 66 { 67 dep[x]=dep[pa]+1,f[x][0]=pa; 68 rep(j,1,19) if((1<<j)<=dep[x]) f[x][j]=f[f[x][j-1]][j-1];else break; 69 ren dfs(to[i],x),rt[x]=merge(rt[x],rt[to[i]]); 70 } 71 int cheq(int x,int mx,int l,int r) 72 { 73 if(!mx) return 1;x=pos[x]; 74 dwn(i,19,0) if((1<<i)<=dep[x]&&mxl[f[x][i]]>=mx) x=f[x][i]; 75 return query(rt[x],1,n,l+mx-1,r); 76 } 77 int main() 78 { 79 n=read(),m=read();scanf("%s",ch+1);las=tot=1;int a,b,c,d,l,r,ans; 80 dwn(i,n,1) extend(ch[i]-'a',n-i+1),pos[n-i+1]=las; 81 rep(i,1,tot) add(fa[i],i);dfs(1,0); 82 while(m--) 83 { 84 a=n-read()+1,b=n+1-read(),c=n+1-read(),d=n+1-read(); 85 swap(a,b);swap(c,d);l=0,r=min(b-a+1,d-c+1),ans=0; 86 for(int mid=l+r>>1;l<=r;mid=l+r>>1) 87 if(cheq(d,mid,a,b)) ans=mid,l=mid+1; 88 else r=mid-1; 89 printf("%d\n",ans); 90 } 91 }
(这种线段树必须要新开点,因为有可能一个点$a$变成了另一个点$b$的儿子,在$c$合并到$b$时,$a$也被合并了