HDU 5785 interesting
给你一个字符串,问满足i<=j<k并且【i,j】和【j,k】都是回文的时候,i*k的sum值是多少。
看了网上的做法,基本都是每个点算贡献,但是有的记录起来比较考验代码能力。
其中有一种做法是这样的:
首先,推公式,自然不用说。由于每个i*k必然出现在一个以j为中心的双回文串中,所以直接统计左右以当前点和下一个点以左与以右的回文个数,相乘即可。
下面是在O(n)时间内算出左右的回文个数。
定义四个数组。
date[1]表示以当前点为右边界的所有回文串的中心的和的两倍。
date[2]表示以当前点为右边界的所有回文串的个数。
date[3]表示以当前点为左边界的所有回文串的中心的和的两倍。
date[4]表示以当前点为左边界的所有回文串的个数。
然后遍历,想到一个点的贡献自然是这个点向左回文范围内所有位置加一,右边也是。因为大的回文小的肯定回文。
在计算个数的时候,也要把区间内的数值都加上i(当前遍历的中心,因为在马拉车里,所以是二倍的中心),以备用。
然后就是核心计算部分了,这里是非常巧妙地地方。
由于要求统计以这个点为左端点的回文右端点,而我们统计的是二倍的中心,其实不难看出对于每一个右端点,要得到它,我们可以把中心加倍,然后减去当前这个值(左端点)就是右端点的所在位置。以此类推,可以求出另一半左端点的位置。
而有人会问怎么保证j<k而没有j=k的情况呢?那么就是关键。因为我在最后加的时候,直接用下一个位置加,以此保证两串不会重叠。
#include <cstdio> #include <cstring> #include <vector> #include <cmath> #include <stack> #include <cstdlib> #include <queue> #include <map> #include <iostream> #include <algorithm> #include <bits/stdc++.h> using namespace std; const int MAXN=((1e6)+5)*2; char str[MAXN]; int p[MAXN]; int MOD=1e9+7; char s[MAXN]; int n,L; void manacher() { int mx=0,id; // printf ("n=%d\n",n); for (int i=1;i<L;i++)//第一个是自己添加的,最后一个是1 { if (mx>i) p[i]=min(p[2*id-i],p[id]+id-i); else p[i]=1; for (;str[i+p[i]]==str[i-p[i]];p[i]++) if (p[i]+i>mx) { mx=p[i]+i; id=i; } } } int date[5][2000100];//1是2倍右中心,2是右个数,3是2倍左中心,4是左个数 void init() { memset(date,0,sizeof(date)); int i,j,k; n=strlen(s); str[0]='@'; str[1]='#'; for (int i=0;i<n;i++) { str[i*2+2]=s[i]; str[i*2+3]='#'; } L=n*2+2; str[L]=0; } void add(int op,int l,int r,int num) { if (l>r) return ; date[op][l]+=num; date[op][l]%=MOD; date[op][r+1]-=num; date[op][r+1]%=MOD; } int main() { while (scanf ("%s",s)!=EOF) { init(); manacher(); // for (int i=1;i<L;i++) // { // printf ("%d ",p[i]); // } // printf ("\n"); for (int i=L-1;i>=1;i--) { add(1,i-p[i]+1,i,i); add(2,i-p[i]+1,i,1); } for (int i=1;i<L;i++) { add(3,i,i+p[i]-1,i); add(4,i,i+p[i]-1,1); } for (int i=1;i<L;i++) { for (int j=1;j<=4;j++) { date[j][i]+=date[j][i-1]; date[j][i]=(MOD+date[j][i])%MOD; } } int ans=0; for (int i=2;i<L-2;i+=2) { int aa,bb; aa=((date[1][i+2]-(long long)date[2][i+2]*((i+2)/2))%MOD+MOD)%MOD; bb=((date[3][i]-(long long)date[4][i]*(i/2))%MOD+MOD)%MOD; ans+=((long long)aa*(long long)bb)%MOD; printf ("i=%d\n",i/2); printf ("%lld\n",((long long)aa*(long long)bb)%MOD); ans%=MOD; } printf ("%d\n",ans); } return 0; }