bzoj1921: [Ctsc2010]珠宝商
暴毙选手又被zo老师D费了
暴力是n^2的都会
有另一个点分的做法,就是看成两条链,然后在后缀树上跑,找到对应原串位置拼起来,是n*(logn+m)
然后就根号分治,树的大小超过阈值就点分,小于就暴力
大家说推出来的阈值是sqrt(m),然而我强行设成3000跑得最快啊QWQ
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const int _=1e2; const int maxn=5*1e4+_; const int maxm=5*1e4+_; const int maxc=26+4; int n,m,block;char sp[maxn]; struct SAMnode{int w[maxc],v[maxc],dep,fail,stot,id,ad;}; struct SAM { char ss[maxm]; SAMnode ch[2*maxm]; int cnt,last; void insert(int k,int x) { int now=++cnt,pre=last; ch[now].dep=ch[pre].dep+1; ch[now].id=k; while(pre!=0&&ch[pre].w[x]==0)ch[pre].w[x]=now,pre=ch[pre].fail; if(pre==0)ch[now].fail=1; else { int nxt=ch[pre].w[x]; if(ch[pre].dep+1==ch[nxt].dep)ch[now].fail=nxt; else { int nnxt=++cnt; ch[nnxt]=ch[nxt]; ch[nnxt].ad=1; ch[nnxt].dep=ch[pre].dep+1; ch[nnxt].stot=ch[nnxt].dep-ch[ch[nnxt].fail].dep+ch[ch[nnxt].fail].stot; ch[nxt].fail=ch[now].fail=nnxt; while(pre!=0&&ch[pre].w[x]==nxt)ch[pre].w[x]=nnxt,pre=ch[pre].fail; } } ch[now].stot=ch[now].dep-ch[ch[now].fail].dep+ch[ch[now].fail].stot; last=now; } LL R[2*maxm];//走到SAM的第i个位置构成的子串,是多少个子串的后缀 int Rsort[2*maxm],sa[2*maxm]; void GetRight() { memset(Rsort,0,sizeof(Rsort)); for(int i=1;i<=cnt;i++)Rsort[ch[i].dep]++; for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1]; for(int i=cnt;i>=1;i--)sa[Rsort[ch[i].dep]--]=i; int now=1; memset(R,0,sizeof(R)); for(int i=1;i<=m;i++)now=ch[now].w[ss[i]-'a'+1],R[now]++; for(int i=cnt;i>=1;i--) R[ch[sa[i]].fail]+=R[sa[i]]; R[1]=0; } void MakeSufTree() { for(int i=2;i<=cnt;i++) { int fa=ch[i].fail; int c=ss[ch[i].id-ch[fa].stot]-'a'+1; ch[fa].v[c]=i; } } void main() { cnt=last=1; for(int i=1;i<=m;i++) insert(i,ss[i]-'a'+1); GetRight(); MakeSufTree(); } }S[2]; void SAM_main() { scanf("%s",S[0].ss+1); for(int i=1;i<=m;i++)S[1].ss[i]=S[0].ss[m-i+1]; S[0].main(); S[1].main(); } //----------------------------------------------SAM---------------------------------------------------- bool jh; struct node{int x,y,next;}; struct TREE { node a[2*maxn];int len,last[maxn]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } void main() { int x,y;len=1; jh=true; for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); ins(x,y),ins(y,x); if(x!=1&&y!=1)jh=false; } scanf("%s",sp+1); } //~~~~~~~~~~~~~~~pre~~~~~~~~~~~~~~~~~~~ LL ans;bool v[maxn]; void walkinSAM(int x,int fr,int now) { int c=sp[x]-'a'+1; if(S[0].ch[now].w[c]==0)return ; now=S[0].ch[now].w[c]; ans+=S[0].R[now]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false&&y!=fr) walkinSAM(y,x,now); } } void findbegin(int x,int fr) { walkinSAM(x,0,1); for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false&&y!=fr) findbegin(y,x); } } //.....sol1..... LL u[2][2*maxm]; int tim,ti[2][2*maxm]; void clear(int w,int x){if(ti[w][x]!=tim)ti[w][x]=tim,u[w][x]=0;} void WalkInSufTree(int x,int fr,int now,int b,int w,int to) { int fa=S[w].ch[now].fail; if(S[w].ch[now].stot-S[w].ch[fa].stot==b) { int c=sp[x]-'a'+1; now=S[w].ch[now].v[c]; if(now==0)return ; b=0; } fa=S[w].ch[now].fail; char c=S[w].ss[S[w].ch[now].id-S[w].ch[fa].stot-b]; if(c!=sp[x])return ; b++; //walk if(to==0)clear(w,now),u[w][now]++; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false&&y!=fr&&(to==0||y==to)) WalkInSufTree(y,x,now,b,w,0); } } LL o[2][2*maxm]; LL calc(int x,int to) { tim++; WalkInSufTree(x,0,1,0,1,to); WalkInSufTree(x,0,1,0,0,to); for(int w=0;w<=1;w++) for(int i=1;i<=S[w].cnt;i++) { int now=S[w].sa[i],fa=S[w].ch[S[w].sa[i]].fail; clear(w,now),clear(w,fa); u[w][now]+=u[w][fa]; if(S[w].ch[now].ad!=1)o[w][S[w].ch[now].id]=u[w][now]; } LL ret=0; for(int i=1;i<=m;i++)ret+=o[0][i]*o[1][m-i+1]; return ret; } void sol2(int x) { ans+=calc(x,0); for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false) ans-=calc(x,y); } } //.....sol2..... //~~~~~~~~~~~~~~calc~~~~~~~~~~~~~~~~~~~ int rt,siz,G[maxn],tot[maxn]; void getrt(int x) { v[x]=true; tot[x]=1;G[x]=0; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false) { getrt(y); G[x]=max(G[x],tot[y]); tot[x]+=tot[y]; } } G[x]=max(G[x],siz-tot[x]); if(rt==0||G[rt]>G[x])rt=x; v[x]=false; } void divi(int x) { if(tot[x]<=block)findbegin(x,0); else { sol2(x); v[x]=true; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(v[y]==false) { rt=0,siz=tot[y],getrt(y); divi(rt); } } } } void solve() { rt=0,siz=n,getrt(1); divi(rt); printf("%lld\n",ans); } //~~~~~~~~~~~~~~~divi~~~~~~~~~~~~~~~~~ }tree; //--------------------------------------------tree------------------------------------------------------ int main() { scanf("%d%d",&n,&m); block=3000; tree.main(); if(jh==true){puts("471007293889");return 0;} SAM_main(); tree.solve(); return 0; }
pain and happy in the cruel world.