1. 程式人生 > >BZOJ[4516][Sdoi2016]生成魔咒 字尾陣列+ST表+線段樹

BZOJ[4516][Sdoi2016]生成魔咒 字尾陣列+ST表+線段樹

首先感謝Sinogi大佬的耐心講解及程式碼

題意就是每次加一個字元,統計本質不同的子串數量
正向新增字元不好做,考慮反著刪字元
在正常情況下,刪掉位置i的一個字元會減少i個子串(j=1j<=isj...i)
但是可能有一些子串沒有被刪乾淨,設j=1j<=kprej,其中pre代表的字首全在刪除i之後的串中出現
也就是在刪除k之前,會有一段區間不應該被減去
這段區間可以線段樹維護,對時間開一個線段樹,每個位置代表多減去的子串數量,要在統計時加回來
而這個k,只可能是i的前驅或是後繼(

SA[rk[i]±1]),分別統計就可以了(因為有的已經被刪過了所以要連結串列維護)
求任意兩個串的LCP要用到ST表
flag:省選以後一定要學SAM QwQ

程式碼如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#include<cmath>
#define N 100050
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'
?-1:f; while(!isdigit(c)); do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c)); return x*f; } typedef int iarr[N]; int n,m,top,x,y; iarr a,b,buck,las,SA,rk,pre,nex,height,pw; int f[N][25]; long long ans[N]; inline bool judge(int x,int y,int k){ return las[x]==las[y] && las[x+k]==las[y+k]; } inline
void Radix_Sort(){ for(int i=1;i<=m;i++) buck[i]=0; for(int i=1;i<=n;i++) buck[rk[las[i]]]++; for(int i=1;i<=m;i++) buck[i]+=buck[i-1]; for(int i=n;i>=1;i--) SA[buck[rk[las[i]]]--]=las[i]; top=m=0; } inline void Get_SA(){ for(int i=1;i<=n;i++){ las[i]=i;rk[i]=a[i]; } Radix_Sort(); for(int i=1;m^n;i<<=1){ for(int j=n-i+1;j<=n;j++) las[++top]=j; for(int j=1;j<=n;j++) if(SA[j]>i) las[++top]=SA[j]-i; Radix_Sort(); for(int j=1;j<=n;j++) las[j]=rk[j]; for(int j=1;j<=n;j++) rk[SA[j]]=judge(SA[j-1],SA[j],i)?m:++m; } for(int i=1,j=0;i<=n;i++,j=max(j-1,0)){ while(a[i+j]==a[SA[rk[i]-1]+j]) j++; height[rk[i]]=j; } } inline void Get_ST(){ pw[0]=1; for(int i=1;pw[i-1]<=n;i++) pw[i]=1<<i; for(int i=1;i<=n;i++) f[i][0]=height[i]; for(int j=1;pw[j]<=n;j++) for(int i=1;i+pw[j-1]<=n;i++) f[i][j]=min(f[i+pw[j-1]][j-1],f[i][j-1]); } inline int RMQ(int i,int j){ int k=log2(j-i+1); return min(f[i][k],f[j-pw[k]+1][k]); } struct Node{ Node *ls,*rs; int addv,x,l,r,sum; Node():ls(NULL),rs(NULL),addv(0),x(0),sum(0){} inline void maintain(){ sum=0; if(ls) sum+=ls->sum; if(rs) sum+=rs->sum; } inline void Pushdown(){ ls->sum+=(ls->r-ls->l+1)*addv; ls->addv+=addv; rs->sum+=(rs->r-rs->l+1)*addv; rs->addv+=addv; addv=0; } }*root; void maketree(int l,int r,Node *&x){ x=new Node;x->l=l;x->r=r; if(l==r) return; int mid=l+r>>1; maketree(l,mid,x->ls);maketree(mid+1,r,x->rs); } inline void Add(int x,int y,int v,Node *k){ if(x>y) return; if(k->l>=x && k->r<=y){ k->sum+=(k->l-k->r+1)*v; k->addv+=v; return; } k->Pushdown(); int mid=k->l+k->r>>1; if(mid>=y) Add(x,y,v,k->ls); else if(mid<x) Add(x,y,v,k->rs); else Add(x,y,v,k->ls),Add(x,y,v,k->rs); k->maintain(); } inline int Query(int x,Node *k){ if(k->l==k->r){ return k->sum; } k->Pushdown(); int mid=k->l+k->r>>1; if(mid>=x) return Query(x,k->ls); else return Query(x,k->rs); } int main(){ n=read(); for(int i=1;i<=n;i++) a[i]=b[i]=read(); sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1; for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+m+1,a[i])-b; Get_SA();Get_ST(); maketree(1,n,root); ans[n]=1ll*n*(n+1)/2; for(int i=1;i<=n;i++){ pre[i]=i-1;nex[i]=i+1; ans[n]-=height[i]; } for(int i=n;i>=1;i--){ x=RMQ(pre[rk[i]]+1,rk[i]);y=RMQ(rk[i]+1,nex[rk[i]]); Add(n-i-x+2,n-i+1,1,root); Add(n-i-y+2,n-i-x+1,1,root); pre[nex[rk[i]]]=pre[rk[i]]; nex[pre[rk[i]]]=nex[rk[i]]; } for(int i=n-1;i;i--){ ans[i]=ans[i+1]-i-1; ans[i]+=Query(n-i,root); } for(int i=1;i<=n;i++){ printf("%lld\n",ans[i]); } return 0; }