1. 程式人生 > >LOJ#2083. 「NOI2016」優秀的拆分

LOJ#2083. 「NOI2016」優秀的拆分

play 個數 nod time vector next 後綴 開頭 pri

$n \leq 30000$的字符串,問其所有子串的所有AABB形式的拆分有多少種。$t \leq 10$組詢問。

$n^3$過80,$n^2$過95,鬼去寫正解。。

$n^2$:先枚舉一次算每個位置結尾的AA形式的子串,再枚舉一次用類似的方法算答案。

正解:類似,記每個位置結尾的AA的子串和每個位置開頭的即可。算這個數組可用如此方法:枚舉A長度$L$,每A個位置標記一個關鍵點。然後相鄰兩個關鍵點$a,b$,找前綴$a,b$的最長公共後綴$p$和後綴$a,b$的最長公共前綴$s$,若$p+s>L$說明這裏有一些A,其中作為起點的範圍是$[a-p+1,a+s-L]$,作為終點的範圍$[b-p+L,b+s-1]$,相當於做了個區間加,可以差分。這樣做的話,復雜度就變成$\frac{n}{1}+\frac{n}{2}+...=n \ln n$了。

找最長公共前後綴,sa或二分hash或sam均可。

技術分享圖片
  1 //#include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 //#include<math.h>
  5 //#include<set>
  6 //#include<queue>
  7 //#include<bitset>
  8 //#include<vector>
  9 #include<algorithm>
 10 #include<stdlib.h>
 11
using namespace std; 12 13 #define LL long long 14 int qread() 15 { 16 char c; int s=0,f=1; while ((c=getchar())<0 || c>9) (c==-) && (f=-1); 17 do s=s*10+c-0; while ((c=getchar())>=0 && c<=9); return s*f; 18 } 19 20 //Pay attention to ‘-‘ , LL and double of qread!!!!
21 22 int T,n; 23 #define maxn 60011 24 char s[maxn]; int f[maxn],g[maxn]; 25 26 struct SAM 27 { 28 struct Node{int pre,ch[26],pos,Max;}a[maxn]; 29 int size,last; 30 void clear() 31 { 32 size=last=0; a[0].pre=-1; a[0].pos=a[0].Max=0; memset(a[0].ch,0,sizeof(a[0].ch)); 33 le=2; memset(first,0,sizeof(first)); 34 } 35 int pp[maxn]; 36 void insert(int c,int p) 37 { 38 int x=++size,y=last; memset(a[x].ch,0,sizeof(a[x].ch)); 39 a[x].Max=a[last].Max+1; a[x].pos=p; pp[p]=x; 40 last=x; 41 for (;~y && !a[y].ch[c];y=a[y].pre) a[y].ch[c]=x; 42 if (!~y) a[x].pre=0; 43 else if (a[a[y].ch[c]].Max==a[y].Max+1) a[x].pre=a[y].ch[c]; 44 else 45 { 46 int z=a[y].ch[c],w=++size; a[w]=a[z]; a[w].pos=p; a[w].Max=a[y].Max+1; 47 a[z].pre=a[x].pre=w; 48 for (;~y && a[y].ch[c]==z;y=a[y].pre) a[y].ch[c]=w; 49 } 50 } 51 struct Edge{int to,next;}edge[maxn<<1]; int first[maxn],le; 52 void in(int x,int y) {Edge &e=edge[le]; e.to=y; e.next=first[x]; first[x]=le++;} 53 int Log[maxn<<1],rmq[22][maxn<<1],dep[maxn],Time,id[maxn]; 54 void predfs(int x) 55 { 56 rmq[0][++Time]=x; id[x]=Time; 57 for (int i=first[x];i;i=edge[i].next) 58 { 59 Edge &e=edge[i]; dep[e.to]=dep[x]+1; 60 predfs(e.to); rmq[0][++Time]=x; 61 } 62 } 63 void pre() 64 { 65 for (int i=1;i<=size;i++) in(a[i].pre,i); 66 Time=0; predfs(0); 67 Log[0]=-1; for (int i=1,to=size*2-1;i<=to;i++) Log[i]=Log[i>>1]+1; 68 for (int j=1;j<=18;j++) 69 for (int i=1,to=size*2-(1<<j);i<=to;i++) 70 rmq[j][i]=dep[rmq[j-1][i]]>dep[rmq[j-1][i+(1<<(j-1))]]?rmq[j-1][i+(1<<(j-1))]:rmq[j-1][i]; 71 } 72 int qq(int x,int y) 73 { 74 x=id[pp[x]]; y=id[pp[y]]; if (x>y) x^=y^=x^=y; int l=Log[y-x+1]; 75 return a[dep[rmq[l][x]]>dep[rmq[l][y-(1<<l)+1]]?rmq[l][y-(1<<l)+1]:rmq[l][x]].Max; 76 } 77 }s1,s2; 78 //1 shi zheng de , 2 shi fan de 79 80 int main() 81 { 82 T=qread(); 83 while (T--) 84 { 85 scanf("%s",s+1); n=strlen(s+1); 86 s1.clear(); s2.clear(); 87 for (int i=1;i<=n;i++) s1.insert(s[i]-a,i); 88 for (int i=n;i;i--) s2.insert(s[i]-a,i); 89 s1.pre(); s2.pre(); 90 91 memset(f,0,sizeof(f)); 92 memset(g,0,sizeof(g)); 93 for (int i=1;i<=n;i++) 94 for (int j=i;j<=n-i;j+=i) 95 { 96 int k=j+i,p=s1.qq(j,k),s=s2.qq(j,k); p=min(p,i); s=min(s,i); 97 if (p+s>i) f[j-p+1]++,f[j+s-i+1]--,g[k-p+i]++,g[k+s]--; 98 } 99 100 for (int i=1,now=0;i<=n;i++) now+=f[i],f[i]=now; 101 for (int i=1,now=0;i<=n;i++) now+=g[i],g[i]=now; 102 LL ans=0; 103 for (int i=1;i<n;i++) ans+=g[i]*1ll*f[i+1]; 104 printf("%lld\n",ans); 105 } 106 return 0; 107 }
View Code

LOJ#2083. 「NOI2016」優秀的拆分