zoukankan      html  css  js  c++  java
  • 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
    ",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 }
  • 相关阅读:
    JSONObject.fromObject--JSON与对象的转换
    oracle 10g学习6
    eclipse中导入svn的三种方式
    网络方面知识收集
    oracle 10g学习之分组函数
    求m阶矩阵的n次幂
    分解质因数
    字符串对比
    十六进制转十进制
    十进制转化十六进制
  • 原文地址:https://www.cnblogs.com/Menhera/p/10791515.html
Copyright © 2011-2022 走看看