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\n",ans);
    }
    return 0;
}

 

posted @ 2020-03-06 17:31  Frank__Chen  阅读(219)  评论(0编辑  收藏  举报