【BZOJ1009】【HNOI2008】GT考试
依旧看人代码写,不过我觉得自己慢慢写一个也可以写成?
原题:
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为
0
N<=10^9,M<=20,K<=1000
一开始完全是懵逼的,后来找到两个解释结合一下就理解了
用a(i,j)表示原串走到i,不吉利的串走到j的方案数
设一个数组b(i,j),表示从(i,j)转移到(i+1,k)的方案数,这个可以用kmp处理,先kmp出next,然后枚举i和j,根据kmp求出k并将b(i+1,k)++
然后这个b表示的就是a中的元素下一步会贡献到哪里
这个东西比较玄,只能勉强意会……
NOIP吧里有一种解释,虽然写法似乎和我的不太一样,引导思路效果不错:
“构造转移矩阵A和列向量B,B=(f[0][0],f[0][1],...,f[0][m]),A可以由DP得到,那么A*B的结果就是(f[1][0],f[1][1],...,f[1][m]),所以A^n*B的结果就是(f[n][0],f[n][1],...,f[n][m])”
然后矩阵快速乘即可
小技巧:使用a&1判断奇偶
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 int n,m,mo,s[30]; 8 int next[30]; 9 int b[30][30],a[30][30],c[30][30]; 10 int ans=0; 11 void kmp(){ 12 int temp=0; next[1]=0; 13 for(int i=2;i<=m;i++){ 14 while(temp && s[temp+1]!=s[i]) temp=next[temp]; 15 if(s[temp+1]==s[i]) temp++; 16 next[i]=temp; 17 } 18 } 19 void get_b(){ 20 int temp; 21 for(int i=0;i<m;i++){//注意j枚举的是下一位,因为这里j是对下一位的转移,所以矩阵乘法从0开始写,比较方便 22 a[i][i]=1; 23 for(int j=0;j<=9;j++){ 24 temp=i; 25 while(temp && s[temp+1]!=j) temp=next[temp]; 26 if(j==s[temp+1]) b[i][temp+1]+=1; 27 else b[i][0]+=1; 28 } 29 } 30 } 31 void fast_mi(){ 32 while(n){ 33 if(n&1){//快速判断奇偶 34 for(int i=0;i<m;i++) 35 for(int j=0;j<m;j++){ 36 c[i][j]=0;//反正也要枚举,就不用memset了 37 for(int k=0;k<m;k++) 38 c[i][j]=(c[i][j]+b[i][k]*a[k][j])%mo; 39 } 40 for(int i=0;i<m;i++) 41 for(int j=0;j<m;j++) 42 a[i][j]=c[i][j]; 43 } 44 n>>=1; 45 for(int i=0;i<m;i++) 46 for(int j=0;j<m;j++){ 47 c[i][j]=0; 48 for(int k=0;k<m;k++) 49 c[i][j]=(c[i][j]+b[i][k]*b[k][j])%mo; 50 } 51 for(int i=0;i<m;i++) 52 for(int j=0;j<m;j++) 53 b[i][j]=c[i][j]; 54 /*for(int i=0;i<m;i++){ 55 for(int j=0;j<m;j++) 56 cout<<a[i][j]<<" "; 57 cout<<endl; 58 }*/ 59 } 60 } 61 int main(){//freopen("ddd.in","r",stdin); 62 memset(b,0,sizeof(b)); 63 cin>>n>>m>>mo; 64 for(int i=1;i<=m;i++){ 65 scanf("%c",&s[i]); while(s[i]<'0'||s[i]>'9') scanf("%c",&s[i]); 66 s[i]-='0'; 67 } 68 kmp(); get_b(); 69 fast_mi(); 70 for(int i=0;i<m;i++) ans=(ans+a[0][i])%mo; 71 cout<<ans<<endl; 72 return 0; 73 }