• [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
    ",res+1ll*n*(n+1)/2);
    82     return 0;
    83 }

    解法二:后缀自动机+线段树合并

    建出parent树,注意到任意两个位置的LCP就是它们在parent树上LCA的mx。于是每个结点上用一棵线段树维护相关信息,同时用一个vector记录子树中的点。线段树的合并直接用普通线段树合并,vector的合并用启发式合并。

    复杂度$O(nlog^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
    ",ans);
    90     return 0;
    91 }
  • 相关阅读:
    个性化联邦学习算法框架发布,赋能AI药物研发
    ES入门 (2) 数据格式/类型
    ES入门 (1) 使用基础(1)安装(1) WIN 单机
    Java 之 JDBC:(十)Spring的JDBCTemplate
    Java 之 JDBC:(九)Apache-DBUtils实现CRUD操作
    Java 之 JDBC:(八)数据库连接池
    Java 之 JDBC:(七)DAO及相关实现类
    Java 之 JDBC:(六)数据库事务
    Java 之 JDBC:(五)批量插入
    第七节:循环结构
  • 原文地址:https://www.cnblogs.com/HocRiser/p/10225775.html
Copyright © 2020-2023  润新知