• hdoj5785(manacher + 差分&&前缀和应用)


    题目链接:https://vjudge.net/problem/HDU-5785

    题意:给定一个长为n的字符串(n<=1e6),求i*k的和,对1e9+7取模。其中[i,j]和[j+1,k]均是回文串。


    思路:

    • 首先用manacher算法处理得到每个点的回文半径p[i]。
    • 然后用L[i]表示以i为回文串右边界的所有左边界的下标的和,R[i]表示以i为回文串左边界的所有右边界的下标的和。那么结果就是L[i]*R[i+1]的和,1<=i<=n-1。
    • 然后就是求L[i],R[i]。拿L[i]来说,对于以i为中心的回文串,对所有 j (属于区间 [ i , i+p[i]-1 ])的L均有贡献,且贡献为i-(j-i) = 2*i-j,则L[j] =2*sigma(i) + k*j,其中i是将j包含在内的回文串的中心,k是这样的回文串的个数。同理,可以证明R[i]的表达式也是这样。
    • 因为manacher算法之后字符串中插入了 '|' ,所以坐标扩大了两倍,所以实际上,L[j] = sigma(i) + k*(j/2),R[j]同理,对于对称轴是 '|' 的情况也成立。
    • 代码中sub[1][i]表示以i为左边界的回文串的中心的和,sub[2][i]表示以i为左边界的回文串的个数,sub[3][i]表示以i为右边界的回文串的中心的和,sub[4][i]表示以i为右边界的回文串的个数。
    • 枚举1到n-1,对于以i为中心的回文串,它对[i-p[i]+1 , i]的sub[1]贡献都是+i(通过差分数组sub[1][l] += i , sub[1][r+1] -= i实现),对sub[2]的贡献都是+1。同理对[i , i+p[i]-1]的sub[3]的贡献都是+i,对sub[4]的贡献都是+1。遍历之后,求差分数组的前缀和来得到相应的值,然后更新答案即可。
    • 总复杂度O(n)。

    (我最开始是想着以i为中心的回文串对[i , i+p[i]-1]的L的贡献是以i为首项,-1为公差的等差数列,于是就用二阶差分来做,但一直wa,QAQ...,也不知道哪错了)

    AC code:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int maxn=1e6+5;
    typedef long long LL;
    const LL MOD=1e9+7;
    int n,p[maxn<<1];
    LL sub[5][maxn<<1],ans;
    char ss[maxn],s[maxn<<1];
    
    void manacher(){
        int mid=0,r=0;
        for(int i=1;i<n;++i){
            if(r>=i) p[i]=min(p[(mid<<1)-i],r-i+1);
            while(s[i-p[i]]==s[i+p[i]]) ++p[i];
            if(i+p[i]>r) r=i+p[i]-1,mid=i;
        }
    }
    
    void add(int p,int l,int r,LL v){
        sub[p][l]=(sub[p][l]+v+MOD)%MOD;
        sub[p][r+1]=(sub[p][r+1]-v+MOD)%MOD;
    }
    
    int main(){
        while(~scanf("%s",ss)){
            ans=0;
            n=strlen(ss);
            s[0]='~',s[1]='|';
            for(int i=0;i<n;++i)
                s[2*i+2]=ss[i],s[2*i+3]='|';
            s[2*n+2]=0;
            n=2*n+2;
            for(int i=0;i<=n;++i)
                p[i]=0,sub[1][i]=0,sub[2][i]=0,sub[3][i]=0,sub[4][i]=0;
            manacher();
            for(int i=1;i<n;++i){
                int t=p[i]-1;
                add(1,i-t,i,i);
                add(2,i-t,i,1);
                add(3,i,i+t,i);
                add(4,i,i+t,1);
            }
            for(int i=1;i<n;++i)
                for(int j=1;j<=4;++j)
                    sub[j][i]=(sub[j][i]+sub[j][i-1]+MOD)%MOD;
            for(int i=2;i<n-2;i+=2){
                LL aa=((sub[1][i+2]-sub[2][i+2]*((i+2)/2))%MOD+MOD)%MOD;
                LL bb=((sub[3][i]-sub[4][i]*(i/2))%MOD+MOD)%MOD;
                ans=(ans+aa*bb%MOD)%MOD;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    登录验证
    登录和退出
    无限极分类信息
    JavaScript 常用函数总结
    Java初学几个程序背诵
    织梦if标签
    PHP常用函数总结(二):
    PHP常用函数总结(一):
    Java开发中的23种设计模式详解(转)
    2015推荐的Android框架
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/12427792.html
Copyright © 2020-2023  润新知