[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 }

 

posted @ 2019-01-05 19:28  HocRiser  阅读(266)  评论(0编辑  收藏  举报