bzoj2160拉拉队排练

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

posted @ 2016-08-17 21:49  YuanZiming  阅读(424)  评论(0编辑  收藏  举报