HDU 5785 Interesting

HDU 5785 Interesting

题目链接:点击打开链接

题目大意:有一个长度为n的串( n106 ),对于 0<ij<kn ,如果 [i,j] [j+1k] 都是回文串,则对答案的贡献为 ik 。求最后的答案,mod 109+7

题解:
首先用Manacher处理出每个字符的回文半径。如果用 L[i] 表示以 i 结尾的回文串的左端点坐标值之和,R[i]表示以i开头的右端点的坐标值之和。那么显然答案就是 n1i=1L[i]R[i+1] ,接下来要求的就是 L[i] R[i] 。以 L[i] 为例,一个中心为 i 半径为l的回文串对属于 [il+1,i] L[i] 值都是有贡献的,且对于 il+1ji 的贡献为 i+(ij) 。那么

L[i]=2jki
其中 j 是包含i的回文串的对称轴坐标,k是i被包含在回文串左半边的次数。因为没有动态的修改,所以只要 O(n) 处理即可。 R[i] 同理。
这题可以直接在Manacher处理的回文半径上直接计算,对于 il+1ji ,对 L[i] 的贡献为 2i2j2=ij2 ,对于对称轴为#的情况同样适用。因此
L[i]=jki2

在计算最后的答案时只要每次+=2即可。

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int mod = 1000000007;
const int maxn=1000010;
typedef long long ll;
char str[maxn];//原字符串
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
int sl;
//转换原始串
int INIT(char *st)
{
    int i,len=strlen(st);
    sl = len;
    tmp[0]='@';//字符串开头增加一个特殊字符,防止越界
    for(i=1;i<=2*len;i+=2)
    {
        tmp[i]='#';
        tmp[i+1]=st[i/2];
    }
    tmp[2*len+1]='#';
    tmp[2*len+2]='$';//字符串结尾加一个字符,防止越界
    tmp[2*len+3]=0;
    return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int MANACHER(char *st,int len)
{
     int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
     for(int i=1;i<=len;i++)
     {
         if(mx>i)
         Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
         else
         Len[i]=1;//如果i>=mx,要从头开始匹配
         while(st[i-Len[i]]==st[i+Len[i]])
         Len[i]++;
         if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
         {
             mx=Len[i]+i;
             po=i;
         }
         ans=max(ans,Len[i]);
     }
     return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}

const int SIZE = maxn << 1;

int C1[SIZE], C2[SIZE], C3[SIZE], C4[SIZE];



void init()
{
    memset(C1,0,sizeof(C1));
    memset(C2,0,sizeof(C2));
    memset(C3,0,sizeof(C3));
    memset(C4,0,sizeof(C4));
}

void add(int l,int r,int c[],int v){
    c[l] += v;
    c[l] %= mod;
    c[r+1] += -v;
    c[r+1] %= mod;
}

void update(int n,int c[]){
    for(int i = 1;i <= n;i++){//更新
        c[i] += c[i-1];
        c[i] %= mod;
    }
    for(int i = 1;i <= n;i++){//求前缀和
        c[i] += c[i-1];
        c[i] %= mod;
    }
}

int query(int l,int r,int c[]){
    int ret = (c[r]-c[l-1]+mod)%mod;
    return ret;
}

int main() {
    while(~scanf("%s",str)) {
        int l = INIT(str);
        MANACHER(tmp, l);
        init();
        int res = 0;

        for(int i = l; i >= 1; i--) {
            //printf("i=%d ",i);
            add((i-Len[i]+1),i,C1,i);
            add((i-Len[i]+1),i,C3,1);
        }
        for(int i = 1; i <= l; i++) {
            add(i,(i+Len[i]-1),C2,i);
            add(i,(i+Len[i]-1),C4,1);
        }
        update(l,C1);
        update(l,C2);
        update(l,C3);
        update(l,C4);
        for(int i = 2,j = 0; i < l-1; i+=2,j++) {
            //printf("%d\n",i);
            int lt = ((query(i+2,i+2,C1) - (ll)query(i+2,i+2,C3)*((i+2)/2))%mod + mod) % mod;
            //left[j] = lt;
            int rt = ((query(i,i,C2) - (ll)query(i,i,C4)*(i/2))%mod + mod ) % mod;
            //right[j] = rt;
            //printf("q1=%d q2=%d q3=%d q4=%d\n",query(i,i,C1),query(i,i,C2),query(i,i,C3),query(i,i,C4));
            //printf("%d %d\n",lt,rt);
            res += ((ll)lt * (ll)rt) %mod;
            res %= mod;
        }
        //for(int i = 0;i < 3;i++) printf("%d ",left[i]); printf("\n");
        //for(int i = 0;i < 3;i++) printf("%d ",right[i]);
        //printf("\n");
        printf("%d\n",res);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值