1. 程式人生 > >【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黃昏下的禮物 dp轉有限狀態自動機+矩陣乘法優化

【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黃昏下的禮物 dp轉有限狀態自動機+矩陣乘法優化

合數 現在 子序列 pri blue gre () div while

神題……
胡亂講述一下思維過程……
首先,讀懂題.
然後,轉化問題為構造一個長度為|T|+n的字符串,使其內含有T這個子序列.
之後,想到一個簡單的dp.由於是回文串,我們就增量構造半個回文串,設f(i,j,k)為構造到第i個位置,從前往後匹配到j,從後往前匹配到k,這樣O(m*m*n)(沒有觀察到其轉移的性質會再乘個26).
再然後,發現不妙,在最後討論奇偶.
(我的思路到此為止……)
接著,觀察其轉移的實質,發現其實dp的過程就是在一個有限狀態自動機上行走,而有限狀態自動機上的狀態就是目前剩下的T,所以我們只不過是要求出從起點到某些點的路徑條數(事先聲明步數).

到了現在,我們的目的只不過是統計到不同終點的路徑條數,那麽:第一步,點集相同的路徑合並起來,點集不同,一定不同;第二步,對於點集相同的路徑們,他們走自環的總數一定,分配不同,一定不同;第三步,對於點集相同且分配相同的路徑們,他們每次走自環,走得不同,一定不同.接著觀察,對於自環數一定的狀態,是一樣的,而且只有3種,並且他們的順序也沒有關系了(可以從組合數的角度看出),至此,對於每一種狀態數量都相同且點集相同的路徑,我們都可以合並,並且還可以算出合並之後的路徑類在不計算內部影響下的貢獻和.
好了,現在該怎麽處理上面最後合並出來的每一種路徑的內部貢獻呢?直接在原自動機上搞,肯定不行,每一種處理一次好一些,但是還是過不了,我們考慮建立一個大圖,利用鄰接矩陣的次冪的性質,我們就可以直接get到每兩個點之間的距離,我們只要保證在這個大圖上能get到所有我們想要的信息就可以了,所以現在我們把之前的自動機合並成一個大自動機就ok了.
最後,把前兩步的信息合並,就好了.
好牛逼的有限狀態自動機啊,感覺就像一個兩端匹配的AC自動機,而且匹配的還是子序列.
還有這個組合數學也是牛的一筆,甚至還利用組合意義合並路徑.
而且那個轉大圖建立鄰接矩陣也是超66666666666666666
(似乎還有組合數做法……看不懂……)
(把dp狀態搞到有限狀態自動機上似乎是套路???)

#pragma GCC optimize("O3")
#include <cstdio>
#include <cstring>
#include <algorithm>
const
int P=10007,N=201; char s[N]; int n,m,f[N][N][2],dp[N][N][N],R,G,B,S,a[N<<1][N<<1],temp[N<<1][N<<1],b[N<<1][N<<1]; inline void DP(){ register int i,j,l,r; dp[1][m][0]=1; for(i=m;i>0;--i) for(l=1,r=i;r<=m;++l,++r) for(j=0;j<=(m-i);++j){ if(!dp[l][r][j])continue; if(s[l]==s[r]){ if(l+1==r) (dp[0][0][j]+=dp[l][r][j])%=P; else if(l==r){ (dp[0][0][j]+=dp[l][r][j])%=P; if(n&1)(f[j][(m-j+1)>>1][0]+=dp[l][r][j])%=P; }else (dp[l+1][r-1][j]+=dp[l][r][j])%=P; }else{ (dp[l+1][r][j+1]+=dp[l][r][j])%=P; (dp[l][r-1][j+1]+=dp[l][r][j])%=P; } } for(i=0;i<=m;++i) (f[i][(m-i+1)>>1][1]+=((n&1)?26:1)*dp[0][0][i])%=P; } #define red(a) (a) #define green(a) (R+(a)) #define blue(a) (R+G+(a)) inline void Multi1(){ memset(temp,0,sizeof(temp)); register int i,j,k; for(i=1;i<=S;++i) for(j=1;j<=S;++j) if(a[i][j]) for(k=1;k<=S;++k) if(b[j][k]) (temp[i][k]+=a[i][j]*b[j][k])%=P; memcpy(b,temp,sizeof(b)); } inline void Multi2(){ memset(temp,0,sizeof(temp)); register int i,j,k; for(i=1;i<=S;++i) for(j=1;j<=S;++j) if(a[i][j]) for(k=1;k<=S;++k) if(a[j][k]) (temp[i][k]+=a[i][j]*a[j][k])%=P; memcpy(a,temp,sizeof(a)); } inline void MUL(){ int i; R=m,G=(m+1)>>1,B=G,S=R+G+B; for(i=1;i<=R;++i){ a[red(i)][red(i)]=24; if(i!=1)a[red(i)][red(i-1)]=1; else a[red(i)][green(1)]=1; } for(i=1;i<=G;++i){ a[green(i)][green(i)]=25; a[green(i)][blue(i)]=1; if(i!=G)a[green(i)][green(i+1)]=1; } for(i=1;i<=B;++i) a[blue(i)][blue(i)]=26; for(i=1;i<=S;++i)b[i][i]=1; int mi=n>>1; while(mi){ if(mi&1)Multi1(); Multi2(),mi>>=1; } } inline void CAL(){ int ans=0,i,j; for(i=0;i<=R;++i) for(j=1;j<=B;++j) if(f[i][j][1]) (ans+=f[i][j][1]*b[i==0?green(1):red(i)][blue(j)])%=P; if(n&1) for(i=0;i<=R;++i) for(j=1;j<=G;++j) if(f[i][j][0]) (ans+=f[i][j][0]*b[i==0?green(1):red(i)][green(j)])%=P; printf("%d\n",ans); } int main(){ scanf("%s%d",s+1,&n); m=strlen(s+1),n+=m; DP(),MUL(),CAL(); return 0; }

【Codeforces 506E】Mr.Kitayuta’s Gift&&【BZOJ 4214】黃昏下的禮物 dp轉有限狀態自動機+矩陣乘法優化