题解 P1659 【[国家集训队]拉拉队排练】

一眼可得PAM

如果没学过PAM的可以看这里:PAM学习小结

我们令PAM上多记录一个信息\(sum\),表示该节点表示串在原串上出现了多少次。

当我们处理完了\(sum\),对于长度\(len\)为奇数的节点的信息\(sum\)计入数组\(a[i]\).

\(a[i]\)为长度为\(i\)的回文子串出现次数。

\(a[i]\)降序排序后累加答案快速幂处理一下即可,不需太多点拨

重点来了

讲一下怎么处理\(sum\)

我们可以发现当一个节点\(u\)\(sum+1\),那么\(fail[u]\)\(sum\)也要\(+1\)

熟悉AC自动机的OIer可以敏锐的察觉到可以用拓扑排序了(例如我

PAM的时候打个标记,最后统一一个拓扑排序向\(fail\)去更新\(sum\)即可

queue<int >q;		//in数组为fail入边数量
void tuopu(){
	for(int i=0;i<=tot;i++)if(in[i]==0)q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		sum[fail[u]]+=sum[u];in[fail[u]]--;
		if(in[fail[u]]==0)q.push(fail[u]);
	}
}

好像没什么问题,多一个拓扑排序就行了

但真的如此吗?

我们观察PAMAC自动机的区别

AC自动机是建好\(Trie\)后再进行\(getFail\)的,\(fail\)的节点编号是会大于自身节点编号

PAM不会出现这种情况,PAM\(fail\)定义不同于AC自动机,构建使用增量法,保证了\(fail\)的节点编号一定小于自身节点编号。

所以就可以不用拓扑排序了,直接一个\(for\)从后到前更新即可

for(int i=tot;i>=0;i--)sum[fail[i]]+=sum[i];

总代码:

#include<bits/stdc++.h>
#define maxn 1010001
#define ll long long
#define mod 19930726
using namespace std;
char s[maxn];
int fail[maxn],len[maxn],trie[maxn][26],trans[maxn];
long long sum[maxn];
int per,slen,tot;
long long a[maxn],K,ans=1;
int getfail(int x,int i){
	while(i-len[x]-1<0||s[i-len[x]-1]!=s[i])x=fail[x];
	return x;
}
int gettrans(int x,int i){
	while(((len[x]+2)<<1)>len[tot]||s[i-len[x]-1]!=s[i])x=fail[x];
	return x;
}
void insert(int u,int i){
	int Fail=getfail(per,i);
	if(!trie[Fail][u]){
		len[++tot]=len[Fail]+2;
		fail[tot]=trie[getfail(fail[Fail],i)][u];
		trie[Fail][u]=tot;
		if(len[tot]<=2)trans[tot]=fail[tot];
		else{
			int Trans=gettrans(trans[Fail],i);
			trans[tot]=trie[Trans][u];
		}
	}
	per=trie[Fail][u];
	sum[per]++;		//记录sum
}
ll qpow(ll n,ll m){
	ll ans=1ll;
	while(m){
		if(m&1){ans=ans*n;ans%=mod;}
		n=n*n;n%=mod;m>>=1;
	}return ans%mod;
}
int main(){
	scanf("%d%lld",&slen,&K);
	scanf("%s",s);
	fail[0]=1;len[1]=-1;tot=1;
	for(int i=0;i<slen;i++)insert(s[i]-'a',i);
	for(int i=tot;i>=1;i--)sum[fail[i]]+=sum[i];		//更新sum
	for(int i=2;i<=tot;i++)a[len[i]]+=sum[i],a[len[i]]%=mod;	//长度处理
	for(int i=slen;i>=1;i--){			//答案处理
		if(i%2==1){
			if(K>=a[i]){
				ans*=qpow(i,a[i]);ans%=mod;
				K-=a[i];
			}else{
				ans*=qpow(i,K);ans%=mod;
				K-=K;
				break;
			}
		}
	}
	if(K==0)			//判-1
	printf("%lld\n",ans%mod);
	else
	printf("-1\n");
	return 0;
}
posted @ 2020-09-19 09:51  Hastieyua  阅读(175)  评论(0编辑  收藏  举报