[Luogu P1659] 拉拉队排练

badge:标签-字符串 标签-数论,数学 标签-前缀和,差分

因为只求奇数长度的回文串,所以不需要中间加字符。

先求出Manacher数组。

然后,对于一个回文中心i,以它为中心的回文串半径长度一定存在p[i],p[i]-2,p[i]-4,...,1各自都有一个。

所以对p[i]这个值,在值域上打一下标记,从大到小做一下后缀和就可以了。

记得用快速幂。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const long long mo=19930726ll;
long long maxf(long long x,long long y){return x>y?x:y;}
long long minf(long long x,long long y){return x<y?x:y;}
long long p[2000010];
char s[2000010];
long long quickpow(long long a,long long x)
{
	long long ans=1ll;
	while(x)
	{
		if(x&1)ans=(ans*a)%mo;
		a%=mo;
		a=(a*a)%mo;
		x>>=1;
	}
	return ans;
}
long long tot[1000010];
int main()
{
	long long n,m,i,j,k,x,y,z;
	long long mx,id,lm,maxn,sum;
	long long ans=1ll;
	scanf("%lld%lld",&n,&m);
	scanf("%s",s+1);
	s[0]='$';
	p[0]=1;
	mx=id=lm=0;
	maxn=1;
	for(i=1;i<=n;i++)
	{
		j=2*id-i;
		if(i<=mx)p[i]=minf(p[j],mx-i+1);
		else p[i]=1;
		while(s[i+p[i]]==s[i-p[i]])p[i]++;
		if(mx<i+p[i]-1)
		{
			mx=i+p[i]-1;
			id=i;
			lm=2*id-mx;
		}
		maxn=maxf(maxn,2*p[i]-1);
		tot[2*p[i]-1]++;
	}
	for(i=maxn;i>=0;i-=2)
	tot[i]+=tot[i+2];
	sum=0ll;
	for(i=1;i<=maxn;i+=2)
	sum+=tot[i];
	if(sum<m)
	{
		printf("-1\n");
		return 0;
	}
	i=maxn;
	while(m)
	{
		if(m>tot[i])
		{
			ans=(1ll*ans*(quickpow(i,tot[i])))%mo;
			m-=tot[i];
			i-=2;
		}
		else
		{
			ans=(1ll*ans*(quickpow(i,m)))%mo;
			break;
		}
	}
	printf("%lld\n",ans);
	return 0;
}
/*
5 3
ababa
ans:45
*/
posted @ 2019-11-04 12:08  Rain_142857  阅读(86)  评论(0编辑  收藏  举报