bzoj2160拉拉队排练
题意:
给一个字符串,求最长的k个回文子串(此处回文子串长度必须为奇数)长度的乘积。字符串长度≤1000000
题解:
先用manacher预处理出第i个字符为中心的最长回文子串一端长度p[i],然后cnt[1]++,cnt[2*p[i]+1]--,最后cnt[i]+=cnt[i-2]求出所有长度的回文子串个数。然后用快速幂求出相同长度的回文子串的长度乘积即可。注意k要开long long!
反思:manacher是一种用来O(n)求每个字符为中心的最长回文子串一端长度的算法(如果要求的回文子串长度可以是偶数,就在原串两端和每两个字符之间插入一个特殊字符,最后答案/2即可)。主要思想是当前求的p[i]和它的某个对称点j是等价的,可以借助p[j]来求。具体看这个。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 1000010 6 #define mod 19930726 7 #define ll long long 8 using namespace std; 9 10 int n,mx,id,p[maxn],cnt[maxn]; char s[maxn]; ll ans,k; 11 ll power(ll a,int b){ 12 if(b==0)return 1; if(b==1)return a; 13 ll c=power(a,b>>1); 14 if(b&1)return c*c%mod*a%mod;else return c*c%mod; 15 } 16 int main(){ 17 scanf("%d%lld",&n,&k); scanf("%s",s+1); mx=0; id=0; 18 inc(i,1,n){ 19 if(mx>i)p[i]=min(p[2*id-i],mx-i);else p[i]=1; 20 while(i-p[i]>=1&&i+p[i]<=n&&s[i-p[i]]==s[i+p[i]])p[i]++; cnt[1]++; cnt[2*p[i]+1]--; 21 if(mx<i+p[i]-1)mx=i+p[i]-1,id=i; 22 } 23 inc(i,3,n)if(i&1)cnt[i]+=cnt[i-2]; ans=1; 24 for(int i=n;i>=1;i--){ 25 if(cnt[i]){ 26 if(k>=cnt[i]){ans=(ans*power(i,cnt[i]))%mod; k-=cnt[i];}else{ans=(ans*power(i,k))%mod; k=0;} 27 if(!k)break; 28 } 29 } 30 if(k)printf("-1");else printf("%lld",ans); return 0; 31 }
20160817