当前位置 博文首页 > 文章内容

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

    作者: 栏目:未分类 时间:2020-09-19 10:00:57

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    一眼可得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;
    }