BZOJ 1009 [HNOI2008]GT考试
1009: [HNOI2008]GT考试
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 2154 Solved: 1327
[Submit][Status][Discuss]
Description
阿申准备报名参加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位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6
Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
Sample Input
111
Sample Output
HINT
Source
题解:做到这道题我很伤心。。。是ac(kmp)+dp
翻了这么多题解还是cjk讲的最好。。。请允许我转一下。。。
字符串上的动态规划:
按顺序处理准考证号每一位,
设f[i][j]表示:准考证号前i位中 后j位与不吉利数的前j位相同时,前i位的方案数
那么答案ans=f[n][0]+f[n][1]+…+f[n][m-1]
f[i][j]的准确含义:
1.f[i][j]表示的每种方案不仅与其后j位有关,还应保证不含不吉利数
2.为避免重复,f[i][j]表示的每种方案都不含长度大于j且与不吉利数的前缀相同 的后缀
否则就会出现:从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案 的情况
状态转移:
f[i][j]只能由f[i-1][k]得到,相当于填完第i-1位后,将其后缀k(长为k的后缀)后面新添一位num,之后这个i位数的 与不吉利数前缀相同的最长后缀是:后缀j
i>=1时:f[i][j]=f[i-1][0]*a[0][j]+f[i-1][1]*a[1][j]+…+f[i-1][m-1]*a[m-1][j]
比如:还是假设不吉利数为123124,那么 f[i][3]=f[i-1][2]+f[i-1][5],因为 f[i-1][2]末尾的*****12不能是**12312,所以需要f[i-1][5]补充
但若不吉利数为123123,那么 f[i][3]=f[i-1][2],因为 f[i][3]末尾的*****123不能是**123123
i==0时:f[0][0]=1,f[0][其他]=0
其中,a[k][j]就表示上面提到的num能取几个值,可以用kmp算法预处理出来,它是一个矩阵
这样就可以不重不漏地计数了
再来个矩阵加速:f[i][j]求法是个线性齐次递推式,可以构造成矩阵,然后加一下速。
这道题要留坑的。。。因为还不是很懂QAQ
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<queue> 6 #include<cstring> 7 #define PAU putchar(' ') 8 #define ENT putchar('\n') 9 using namespace std; 10 const int maxn=30; 11 int n,mod,len,nxt[maxn];char str[maxn]; 12 struct matrix{int f[maxn][maxn];matrix(){memset(f,0,sizeof(f));}}m,ans; 13 matrix operator*(const matrix&a,const matrix&b){ 14 matrix c; 15 for(int i=0;i<len;i++) 16 for(int j=0;j<len;j++) 17 for(int k=0;k<len;k++) 18 c.f[i][j]=(c.f[i][j]+a.f[i][k]*b.f[k][j])%mod; 19 return c; 20 } 21 void kmp(){ 22 int j=0;nxt[1]=0; 23 for(int i=2;i<=len;i++){ 24 while(j&&str[j+1]!=str[i]) j=nxt[j]; 25 if(str[j+1]==str[i])j++;nxt[i]=j; 26 }return; 27 } 28 void dp(){ 29 for(int i=0;i<len;i++) 30 for(int j=0;j<10;j++){ 31 int k; 32 for(k=i;k;k=nxt[k]) if(str[k+1]-'0'==j) break; 33 if(str[k+1]-'0'==j) k++; 34 m.f[i][k]=(m.f[i][k]+1)%mod; 35 } 36 return; 37 } 38 void pow(){ 39 for(int i=0;i<len;i++) ans.f[i][i]=1; 40 for(;n;n>>=1,m=m*m)if(n&1) ans=ans*m;return; 41 } 42 inline int read(){ 43 int x=0,sig=1;char ch=getchar(); 44 while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();} 45 while(isdigit(ch)) x=10*x+ch-'0',ch=getchar(); 46 return x*sig; 47 } 48 inline void write(int x){ 49 if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x; 50 int len=0,buf[15];while(x) buf[len++]=x%10,x/=10; 51 for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return; 52 } 53 int main(){ 54 n=read();len=read();mod=read();scanf("%s",str+1);kmp();dp();pow(); 55 int res=0;for(int i=0;i<len;i++) res=(res+ans.f[0][i])%mod; 56 write(res); 57 return 0; 58 }