[Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257
解法一:后缀数组
显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n-1)/2。
答案=重复子串个数-相邻或相交重复子串个数。
前者单调栈直接求解,注意细节,重点在后者。
由于是有关相交的计数问题,考虑类似[NOI2016]优秀的拆分的设关键点的做法。
枚举两个串的偏移量k,每k个位置设一个关键点,我们需要保证任意两个相距为k的重复子串都在且仅在它们覆盖的第一个关键点处被计算一次。
求出每相邻两个关键点的LCP和LCS,发现答案是一个等差数列,注意式子的推导,不能重复计算。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=300010; 9 int n,tot,top,s[N],b[N],stk[N],lg[N]; 10 ll res,sm; 11 12 struct SA{ 13 int s[N],c[N],x[N],y[N],sa[N],rk[N],st[N][20],h[N]; 14 bool Cmp(int a,int b,int l){ return a+l<=n && b+l<=n && y[a]==y[b] && y[a+l]==y[b+l]; } 15 16 void build(int m){ 17 memset(y,0,sizeof(y)); 18 rep(i,0,m) c[i]=0; 19 rep(i,1,n) c[x[i]=s[i]]++; 20 rep(i,1,m) c[i]+=c[i-1]; 21 for (int i=n; i; i--) sa[c[x[i]]--]=i; 22 for (int k=1,p=0; p<n; k<<=1,m=p){ 23 p=0; 24 rep(i,n-k+1,n) y[++p]=i; 25 rep(i,1,n) if (sa[i]>k) y[++p]=sa[i]-k; 26 rep(i,0,m) c[i]=0; 27 rep(i,1,n) c[x[y[i]]]++; 28 rep(i,1,m) c[i]+=c[i-1]; 29 for (int i=n; i; i--) sa[c[x[y[i]]]--]=y[i]; 30 rep(i,1,n) y[i]=x[i]; x[sa[p=1]]=1; 31 rep(i,2,n) x[sa[i]]=Cmp(sa[i-1],sa[i],k) ? p : ++p; 32 } 33 } 34 35 void height(){ 36 int k=0; 37 rep(i,1,n) rk[sa[i]]=i; 38 rep(i,1,n){ 39 for (int j=sa[rk[i]-1]; i+k<=n && j+k<=n && s[i+k]==s[j+k]; k++); 40 h[rk[i]]=k; if (k) k--; 41 } 42 rep(i,1,n) st[i][0]=h[i]; 43 rep(j,1,lg[n]) rep(i,1,n-(1<<j)+1) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); 44 } 45 46 int que(int a,int b){ 47 int x=rk[a],y=rk[b]; 48 if (x==y) return n-a+1; 49 if (x>y) swap(x,y); 50 x++; int t=lg[y-x+1]; 51 return min(st[x][t],st[y-(1<<t)+1][t]); 52 } 53 }sa1,sa2; 54 55 int LCP(int l,int r){ return sa1.que(l,r); } 56 int LCS(int l,int r){ return sa2.que(n-r+1,n-l+1); } 57 58 int main(){ 59 freopen("P5161.in","r",stdin); 60 freopen("P5161.out","w",stdout); 61 scanf("%d",&n); 62 rep(i,2,n) lg[i]=lg[i>>1]+1; 63 rep(i,1,n) scanf("%d",&s[i]); 64 n--; rep(i,1,n) b[i]=s[i]=s[i+1]-s[i]; 65 sort(b+1,b+n+1); tot=unique(b+1,b+n+1)-b-1; 66 rep(i,1,n) s[i]=lower_bound(b+1,b+tot+1,s[i])-b; 67 rep(i,1,n) sa1.s[i]=sa2.s[n-i+1]=s[i]; 68 sa1.build(tot); sa2.build(tot); sa1.height(); sa2.height(); 69 rep(i,1,n+1){ 70 res+=sm; 71 for (; top && sa1.h[stk[top]]>=sa1.h[i]; top--) 72 sm-=1ll*(sa1.h[stk[top]]-sa1.h[i])*(stk[top]-stk[top-1]); 73 sm+=sa1.h[stk[++top]=i]; 74 } 75 rep(i,1,n){ 76 for (int j=i; j+i<=n; j+=i){ 77 int a=min(i,LCS(j,j+i)),b=LCP(j,j+i),l=min(a-1,b+a-i); 78 if (l>=0) res-=1ll*(l+1)*(b+a-i)-1ll*l*(l+1)/2; 79 } 80 } 81 printf("%lld\n",res+1ll*n*(n+1)/2); 82 return 0; 83 }
解法二:后缀自动机+线段树合并
建出parent树,注意到任意两个位置的LCP就是它们在parent树上LCA的mx。于是每个结点上用一棵线段树维护相关信息,同时用一个vector记录子树中的点。线段树的合并直接用普通线段树合并,vector的合并用启发式合并。
复杂度$O(n\log^2 n)$,但由于常数小所以不会被卡。(当然用时是解法一的三倍)
1 #include<map> 2 #include<cstdio> 3 #include<vector> 4 #include<algorithm> 5 #define lson ls[x],L,mid 6 #define rson rs[x],mid+1,R 7 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 8 #define For(i,u) for (int i=h[u]; i; i=nxt[i]) 9 typedef long long ll; 10 using namespace std; 11 12 const int N=1000010,M=20000010; 13 ll ans,v[M],vs[M]; 14 int n,p,np,lst=1,nd=1,nd2,cnt,a[N],pos[N],rt[N],mx[N]; 15 int fa[N],ls[M],rs[M],to[N<<1],nxt[N<<1],h[N]; 16 map<int,int>son[N]; 17 vector<int>ve[N],*f[N]; 18 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 19 20 void ext(int c,int x){ 21 p=lst; lst=np=++nd; mx[np]=mx[p]+1; pos[x]=np; 22 while (p && !son[p][c]) son[p][c]=np,p=fa[p]; 23 if (!p) fa[np]=1; 24 else{ 25 int q=son[p][c]; 26 if (mx[q]==mx[p]+1) fa[np]=q; 27 else{ 28 int nq=++nd; mx[nq]=mx[p]+1; 29 son[nq]=son[q]; fa[nq]=fa[q]; fa[q]=fa[np]=nq; 30 while (p && son[p][c]==q) son[p][c]=nq,p=fa[p]; 31 } 32 } 33 } 34 35 void mdf(int &x,int L,int R,int k){ 36 if (!x) x=++nd2; 37 v[x]++; vs[x]+=k; 38 if (L==R) return; 39 int mid=(L+R)>>1; 40 if (k<=mid) mdf(lson,k); else mdf(rson,k); 41 } 42 43 ll que(int x,int L,int R,int l,int r,int k){ 44 l=max(l,L); r=min(r,R); 45 if(!x || l>r || r<1 || l>n) return 0; 46 if (L==l && r==R) return k ? vs[x] : v[x]; 47 int mid=(L+R)>>1; 48 if (r<=mid) return que(lson,l,r,k); 49 else if (l>mid) return que(rson,l,r,k); 50 else return que(lson,l,mid,k)+que(rson,mid+1,r,k); 51 } 52 53 int merge(int x,int y){ 54 if (!x || !y) return x|y; 55 v[x]+=v[y]; vs[x]+=vs[y]; 56 ls[x]=merge(ls[x],ls[y]); 57 rs[x]=merge(rs[x],rs[y]); 58 return x; 59 } 60 61 void dfs(int u){ 62 int k=mx[u]; 63 For(i,u){ 64 int v=to[i]; dfs(v); 65 if (f[u]->size()<f[v]->size()) swap(f[u],f[v]),swap(rt[u],rt[v]); 66 int ed=f[v]->size()-1; 67 rep(j,0,ed){ 68 int x=f[v]->at(j); 69 ll A=que(rt[u],1,n,x-k-1,x-2,0)*(x-1)-que(rt[u],1,n,x-k-1,x-2,1)+que(rt[u],1,n,1,x-k-2,0)*k; 70 ll B=que(rt[u],1,n,x+2,x+k+1,1)-que(rt[u],1,n,x+2,x+k+1,0)*(x+1)+que(rt[u],1,n,x+k+2,n,0)*k; 71 ans+=A+B; 72 } 73 rep(j,0,ed) f[u]->push_back(f[v]->at(j)); 74 rt[u]=merge(rt[u],rt[v]); 75 } 76 } 77 78 int main(){ 79 freopen("P5161.in","r",stdin); 80 freopen("P5161.out","w",stdout); 81 scanf("%d",&n); 82 rep(i,1,n) scanf("%d",&a[i]); 83 n--; rep(i,1,n) a[i]=a[i+1]-a[i]; 84 ans=1ll*n*(n+1)>>1; 85 rep(i,1,n) ext(a[i],i); 86 rep(i,2,nd) add(fa[i],i); 87 rep(i,1,nd) f[i]=&ve[i]; 88 rep(i,1,n) f[pos[i]]->push_back(i),mdf(rt[pos[i]],1,n,i); 89 dfs(1); printf("%lld\n",ans); 90 return 0; 91 }