zoukankan      html  css  js  c++  java
  • 题解 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
    ",ans%mod);
    	else
    	printf("-1
    ");
    	return 0;
    }
    
  • 相关阅读:
    linux 文件系统管理三部曲之二:创建文件系统
    linux 文件系统管理三部曲之一:磁盘分区
    Django 链接MySQL及数据操作
    redis事务
    redis配置文件详解
    redis中hash数据类型
    redis的基础知识
    redis两种持久化方式RDB和AOF
    git命令
    .gitignore中添加的某个忽略文件并不生效
  • 原文地址:https://www.cnblogs.com/hyfhaha/p/13694781.html
Copyright © 2011-2022 走看看