Hdu 5785 Interesting(Manacher+区间处理)

58 篇文章 0 订阅
37 篇文章 0 订阅

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5785

思路:

1.L[i]表示以i开始的所有回文的结束位置和,R[i]表示以i位置结尾的所有回文开始位置和,则答案为sigma(R[i-1]*L[i])(x1*y1+x1*y2+...x1*yn+x2*y1+x2*y2+.....x2*yn+......=(x1+x2+....xn)*y1+(x1+x2+...+xn)*y2+......=(x1+x2+.....+xn)*(y1+y2+....+ym))。

2.首先使用Manacher算法求出以每个字母i为对称轴的回文半径pos[i]。对于L:则对应i,字符串[i-pos[i],i+pos[i]]、[i-pos[i]+1,i+pos[i]-1]、[i-pos[i]+2,i+pos[i]-2]、.......、[i,i]为回文串,对应位置i-pos[i]应加上i+pos[i]、i-pos[i]+1应加上i+pos[i]-1、.....、i应加上i。即为对应每一个区间[i-pos[i],i]依次加上以i+pos[i]为首项,-1为公差的等差数列。R数组同理。

3.若对区间[l,r]加上同一个数x,则只需设置标记:对应首项a[l]+=x,尾项a[r+1]-=x,依次递推即可。若对区间加上一首项为x,公差为d的一等差数列,则需设置一数组sub,表示每项的增量。设置a[l]+=x,a[r+1]-=等差数列尾项,sub[l]+=-d,sub[r+1]+=d。依次递推出增量sub[i]+=sub[i-1],对于每项a[i],a[i]+=a[i-1]-sub[i]。

4.由于数组操作为添加符号之后的字符串,则对于原串ansL[i]=L[i]/2,ansR[i]=R[i]/2。由于最终结果取模,此处应乘以逆元500000004(错了好久。。。。)。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug
using namespace std;

typedef long long LL;

const int inv=500000004;
const int mod=1000000007;
const int maxn=2000000+5;

int l;
LL Mp[maxn];
LL L[maxn],subL[maxn];
LL R[maxn],subR[maxn];
char Ma[maxn],st[maxn];
LL ansL[maxn],ansR[maxn];

void Manacher(char s[],int len)
{
    l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0; i<len; i++)
    {
        Ma[l++]=s[i];
        Ma[l++]='#';
    }
    Ma[l]=0;
    for(int i=0;i<=l;i++) Mp[i]=0;
    LL mx=0,id=0;
    for(int i=0; i<l; i++)
    {
        Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]]) Mp[i]++;
        if(i+Mp[i]>mx)
        {
            mx=i+Mp[i];
            id=i;
        }
    }
}

void solve()
{
    for(int i=0; i<=l; i++)
    {
        L[i]=R[i]=subL[i]=subR[i]=0;
    }

    for(int i=1; i<l; i++)
    {
        Mp[i]--;

        L[i-Mp[i]]+=i+Mp[i];
        L[i+1]-=i;
        subL[i-Mp[i]+1]+=1;
        subL[i+1]-=1;

        R[i+Mp[i]]+=i-Mp[i];
        R[i-1]-=i;
        subR[i+Mp[i]-1]-=1;
        subR[i-1]+=1;

    }

    for(int i=l-1; i>=1; i--)
    {
        subR[i]+=subR[i+1];subR[i]%=mod;
        R[i]+=R[i+1]-subR[i]+mod;R[i]%=mod;

    }

    for(int i=1; i<l; i++)
    {
        subL[i]+=subL[i-1];subL[i]%=mod;
        L[i]+=L[i-1]-subL[i]+mod;L[i]%=mod;
    }

    int cnt=0;
    for(int i=2; i<l; i+=2)
    {
        ansL[++cnt]=L[i]*inv%mod;
        ansR[cnt]=R[i]*inv%mod;
    }

    LL ans=0;
    for(int i=2; i<=cnt; i++)
    {
        ans+=(LL)ansR[i-1]*(LL)ansL[i];
        ans%=mod;
    }
    printf("%lld\n",ans);
}

int main()
{
#ifdef debu
    freopen("in.txt","r",stdin);
    freopen("out.out","w",stdout);
#endif // debug
    while(scanf("%s",st)!=EOF)
    {
        Manacher(st,strlen(st));
        solve();
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值