題解 P1659 【[國家集訓隊]拉拉隊排練】
阿新 • • 發佈:2020-09-19
一眼可得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]);
}
}
好像沒什麼問題,多一個拓撲排序就行了
但真的如此嗎?
我們觀察PAM
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; }