LOJ2083 [NOI2016] 优秀的拆分 【哈希】【调和级数】

题目分析:

好题!我们发现题目实际是要求出从某个左端点开始跑出去的BB型有多少个和从某个右端点开始跑出去的AA型有多少个。

发现这个问题是对称的,所以只考虑从左端点跑出去的BB型有多少个就可以了。

我们不妨考虑长度为$k$的BB型,那么我们把字符串每$k$个化成一个整体,然后如果从$i$开始存在一个长度为$k$的BB型,就等价于$i$开始这个整体的后缀等于下一个整体的后缀,下一个整体的后缀等于下下个整体的前缀,所以我们用哈希来求出最长后缀和最长前缀就可以做了。

 

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int maxn = 102000;
 5 
 6 int n;
 7 char str[maxn];
 8 int pre[maxn],suf[maxn]; // longest qianzhui longest houzhui
 9 int f[maxn],g[maxn];
10 
11 namespace HASH{
12     int ph[2][maxn],bs[2][maxn];
13     const int base = 37;
14     const int mod1 = 1004535809,mod2 = 999911659;
15     void buildhash(){
16     ph[0][1] = ph[1][1] = str[0]-'a'+1;
17     bs[0][0] = bs[1][0] = 1;
18     for(int i=1;i<n;i++){
19         ph[0][i+1] = (1ll*base*ph[0][i]+str[i]-'a'+1)%mod1;
20         ph[1][i+1] = (1ll*base*ph[1][i]+str[i]-'a'+1)%mod2;
21     }
22     for(int i=1;i<=n;i++) bs[0][i]=1ll*bs[0][i-1]*base%mod1;
23     for(int i=1;i<=n;i++) bs[1][i]=1ll*bs[1][i-1]*base%mod2;
24     }
25     int pd(int l1,int r1,int l2,int r2){
26     int z1=ph[0][r1+1]-1ll*bs[0][r1-l1+1]*ph[0][l1]%mod1;if(z1<0)z1+=mod1;
27     int z2=ph[1][r1+1]-1ll*bs[1][r1-l1+1]*ph[1][l1]%mod2;if(z2<0)z2+=mod2;
28     int y1=ph[0][r2+1]-1ll*bs[0][r2-l2+1]*ph[0][l2]%mod1;if(y1<0)y1+=mod1;
29     int y2=ph[1][r2+1]-1ll*bs[1][r2-l2+1]*ph[1][l2]%mod2;if(y2<0)y2+=mod2;
30     if(z1 == y1 && z2 == y2) return true;
31     else return false;
32     }
33     int maxlen(int st1,int st2,int dr){
34     if(st2 >=n) return 0;
35     int tl=1,tr=(dr==0?st1+1:n-st2+1);
36     if(str[st1] != str[st2]) return 0;
37     while(tl < tr){
38         int mid = (tl+tr+1)/2;
39         int l1,r1,l2,r2;
40         if(dr == 0){r1=st1,r2=st2;l1=st1-mid+1,l2=st2-mid+1;}
41         else{l1=st1,l2=st2;r1=st1+mid-1,r2=st2+mid-1;}
42         if(pd(l1,r1,l2,r2)) tl = mid;
43         else tr = mid-1;
44     }
45     return tl;
46     }
47 }
48 
49 
50 void init(){
51     memset(pre,0,sizeof(pre));
52     memset(suf,0,sizeof(suf));
53     memset(HASH::ph,0,sizeof(HASH::ph));
54     memset(f,0,sizeof(f));
55     memset(g,0,sizeof(g));
56 }
57 
58 void work(){
59     HASH::buildhash();
60     for(int i=1;i<=n/2;i++){
61     int k = 0;
62     for(int j=0;j<n;j+=i){
63         k++;
64         if(j+i < n){pre[k] = min(i,HASH::maxlen(j,j+i,1));}
65         if(j-i >=0){suf[k] = min(i,HASH::maxlen(j-1,j+i-1,0));}
66     }
67     for(int j=2,st=i;j<=k;j++,st+=i){
68         if(pre[j] + suf[j] < i || suf[j] == 0) continue;
69         int l = st-suf[j],r = min(st-1,(st-i)+pre[j]);
70         f[l]++; f[r+1]--;
71     }
72     for(int j=1,st=0;j<k;j++,st+=i){
73         if(pre[j] + suf[j] < i || pre[j] == 0) continue;
74         int l = max(st+i,(st+i-1+(i-suf[j]))),r = (st+i-1+pre[j]);
75         g[l]++; g[r+1]--;
76     }
77     for(int j=1;j<=k;j++) pre[j] = suf[j] = 0;
78     }
79     for(int i=1;i<n;i++) f[i] = f[i-1] + f[i];
80     for(int i=1;i<n;i++) g[i] = g[i-1] + g[i];
81     long long ans = 0;
82     for(int i=1;i<n;i++){ans += 1ll*f[i]*g[i-1];}
83     printf("%lld\n",ans);
84 }
85 
86 int main(){
87     int Tmp; scanf("%d",&Tmp);
88     while(Tmp--){
89     init();
90     scanf("%s",str);
91     n = strlen(str);
92     work();
93     }
94     return 0;
95 }

 

posted @ 2019-04-29 17:03  menhera  阅读(203)  评论(0编辑  收藏  举报