[補檔][HNOI 2008]GT考試
題目
阿申準備報名參加GT考試,準考證號為N位數X1X2....Xn(0<=Xi<=9),他不希望準考證號上出現不吉利的數字。
他的不吉利數學A1A2...Am(0<=Ai<=9)有M位,不出現是指X1X2...Xn中沒有恰好一段等於A1A2...Am. A1和X1可以為0
INPUT
第一行輸入N,M,K.接下來一行輸入M位的數。 N<=10^9,M<=20,K<=1000
OUTPUT
阿申想知道不出現不吉利數字的號碼有多少種,輸出模K取余的結果.
SAMPLE
INPUT
4 3 100
111
OUTPUT
81
解題報告
這道題一開始真心沒有什麽思路,後來我跟兩個dalao一起商(luan)談(gao)了一個來小時,終於搞了出來= =
首先,我們我們考慮兩個串(從短到長)
第一個串為不斷增加的準考證號,第二個串為不吉利的號碼,第一個串的後綴與第二個串的前綴為重復部分,那麽我們很容易得出遞推關系
先扯出來,考慮兩個集合,一個集合為不吉利的號碼,一個集合為吉利的號碼。我們只考慮後綴(正確性顯然,因為長度長的串一定是由長度短的串遞推過來的,所以如果前面有不吉利串,一定被前面的串卡掉了),當後綴包含了m個不吉利串,該串一定是不吉利的。那麽,後綴包含0~m-1個不吉利串的前綴的串一定是吉利的。所以,我們把求不吉利的串 轉化為 求 後綴包含不吉利串0~m-1 的 串 的 方案數
然而與字符串有啥關系?
考慮這樣一個不吉利串
123124
當你的後綴帶了2時,你怎麽知道你的後幾位是12還是12312?所以,加一位不吉利數不代表直接在後面加了一位。
那麽,問題來了,如何表示這些奇(chun)奇(de)怪(bu)怪(xing)的轉移?
考慮一個遞推矩陣,設dp[i][j]為第i個號碼匹(zhuan)配(yi)到第j個不吉利數字的方案數,設a[k][i]為k位後加一個數轉移到j的方案數,我們可以輕易的得出遞推關系:
dp[i][j]=/sumdp[i-1][k]*a[k][j]
用KMP構造初始矩陣,矩陣快速冪得到遞推結果。
為什麽是矩陣快速冪?
這個問題很簡單。我們知道,矩陣乘是這樣寫的:
1 martrix tmp; 2 for(int i=0;i<n;i++) 3 for(int j=0;j<n;j++){ 4 tmp.data[i][j]=0; 5 for(int k=0;k<n;k++){ 6 tmp.data[i][j]+=(a.data[i][k]*b.data[k][j]); 7 tmp.data[i][j]%=mod; 8 } 9 } 10 return tmp;View Code
那麽問題就簡單起來了,考慮一個3*3的初始矩陣,第i行,第j列表示從第i位加一個數字轉移到第j為數字的方案數,那麽以該矩陣的平方的第一行第一列的數為例,(設初始矩陣為a,該矩陣為x):
x[1][1]=a[1][1]×a[1][1]+a[1][2]×a[2][1]+a[1][3]×a[3][1];
因為是平方得到的矩陣,所以代表了轉移兩步的狀態,我們知道,x[1][1]代表了從第一位經兩步轉移到第一位的方案數,由加法原理可知:
1->1(經兩步)=(1->1->1)+(1->2->1)+(1->3->1)
而又由乘法原理可知:
1->2->1=(1->2)×(2->1)
那麽正確性就很顯然了,由矩陣快速冪的遞推關系可知,最終結果即為第一行的數的和
至於KMP,把10個數字扔進去亂搞就是了= =
記得要模k= =
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 int n,m,mod; 6 char s[25]; 7 int kp[25]; 8 inline void get_kp(){ 9 kp[0]=0; 10 kp[1]=0; 11 int k(0); 12 for(int i=2;i<=m;i++){ 13 while(k&&s[k]!=s[i-1]) 14 k=kp[k]; 15 if(s[k]==s[i-1]) 16 k++; 17 kp[i]=k; 18 } 19 } 20 struct node{ 21 int data[25][25]; 22 node(){ 23 memset(data,0,sizeof(data)); 24 } 25 node operator*(node &a){ 26 node tmp; 27 for(int i=0;i<m;i++) 28 for(int j=0;j<m;j++){ 29 tmp.data[i][j]=0; 30 for(int k=0;k<m;k++){ 31 tmp.data[i][j]+=(data[i][k]*a.data[k][j]); 32 tmp.data[i][j]%=mod; 33 } 34 } 35 return tmp; 36 } 37 node operator*=(node &a){ 38 *this=*this*a; 39 return *this; 40 } 41 }a,sing; 42 ostream& operator<<(ostream &out,node &a){ 43 for(int i=0;i<m;i++){ 44 for(int j=0;j<m;j++) 45 out<<a.data[i][j]<<‘ ‘; 46 out<<endl; 47 } 48 return out; 49 } 50 int main(){ 51 scanf("%d%d%d%s",&n,&m,&mod,s); 52 get_kp(); 53 for(int i=0;i<m;i++) 54 for(int j=0;j<=9;j++){ 55 int k(i); 56 while(k&&(j+‘0‘)!=s[k]) 57 k=kp[k]; 58 if(j+‘0‘==s[k]) 59 k++; 60 if(k!=m){ 61 a.data[i][k]++; 62 a.data[i][k]%=mod;//cout<<‘*‘; 63 } 64 }//cout<<a<<endl; 65 /*for(int i=0;i<m;i++){ 66 for(int j=0;j<m;j++) 67 cout<<a[i][j]<<‘ ‘; 68 cout<<‘\n‘; 69 }*/ 70 for(int i=0;i<m;i++) 71 sing.data[i][i]=1; 72 int tmp(n); 73 while(tmp){ 74 if(tmp&1) 75 sing*=a; 76 a*=a; 77 //cout<<tmp<<endl; 78 //cout<<a<<endl<<sing<<endl; 79 tmp>>=1; 80 } 81 int ans(0); 82 for(int i=0;i<m;i++){ 83 ans=(ans+sing.data[0][i])%mod; 84 //cout<<ans<<endl; 85 } 86 cout<<ans; 87 //while(1); 88 }View Code
ps:2017-6-14 晚 講題用題解
pss:調矩陣快調死了= =,最後發現初始矩陣求錯了= =
[補檔][HNOI 2008]GT考試