一本通1646GT 考试
1646:GT 考试
时间限制: 1000 ms 内存限制: 524288 KB【题目描述】
阿申准备报名参加 GT 考试,准考证号为 n 位数 X1X2⋯Xn(0≤Xi≤9),他不希望准考证号上出现不吉利的数字。
他的不吉利数字 A1A2⋯Am(0≤Ai≤9) 有 m 位,不出现是指 X1X2⋯Xn 中没有恰好一段等于 A1A2⋯Am ,A1和 X1 可以为 0。
【输入】
第一行输入 n,m,K,接下来一行输入 m 位的数。
【输出】
阿申想知道不出现不吉利数字的号码有多少种,输出模 K 取余的结果。
【输入样例】
4 3 100
111
【输出样例】
81
【提示】
数据范围与提示:
对于全部数据,1≤n≤109,1≤m≤20,2≤K≤1000。
sol:此题应该是先想到暴力的做法再用矩阵乘法优化的
所以暴力的思想很重要
dp[i][j]表示到第i位,匹配了j个的方案数
先预处理出f[i][j]表示已经匹配了i个,加一个数字变成匹配了j个方案数,i,j<=m-1,用kmp搞搞
转移就不难了,dp[i][j]+=dp[i-1][k]*f[k][j],是不是很像矩阵乘法2333
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=100005,M=25; int n,m,Mod; int Num[25],f[25][25]; int dp[N][M]; inline void Ad(int &x,int y) { x+=y; x-=(x>=Mod)?Mod:0; return; } int Next[25]; inline void Pre_f() { int i,j=0; Next[1]=0; for(i=2;i<=m;i++) { while((Num[j+1]!=Num[i])&&j) j=Next[j]; if(Num[j+1]==i) j++; Next[i]=j; } for(i=0;i<m;i++) { for(j=0;j<=9;j++) { int Now=i; while((Num[Now+1]!=j)&&Now) Now=Next[Now]; if(Num[Now+1]==j) Now++; f[i][Now]++; } } /* for(i=0;i<m;i++,puts("")) { for(j=0;j<m;j++) W(f[i][j]); } puts(""); */ return; } int main() { int i,j,k,ans=0; R(n); R(m); R(Mod); for(i=1;i<=m;i++) { char ch=' '; while(!isdigit(ch)) ch=getchar(); Num[i]=ch-'0'; } Pre_f(); dp[0][0]=1; for(i=1;i<=n;i++) { for(j=0;j<=m-1;j++) { for(k=0;k<=m-1;k++) { Ad(dp[i][j],dp[i-1][k]*f[k][j]%Mod); } } } for(i=0;i<m;i++) Ad(ans,dp[n][i]); Wl(ans); return 0; } /* input 4 3 100 111 output 81 */
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=100005,M=25; int n,m,Mod; int Num[25],f[25][25]; int ans[25][25],power[25][25],a[25][25],c[25][25]; inline void Ad(int &x,int y) { x+=y; x-=(x>=Mod)?Mod:0; return; } int Next[25]; inline void Pre_f() { int i,j=0; Next[1]=0; for(i=2;i<=m;i++) { while((Num[j+1]!=Num[i])&&j) j=Next[j]; if(Num[j+1]==Num[i]) j++; Next[i]=j; } for(i=0;i<m;i++) { for(j=0;j<=9;j++) { int Now=i; while((Num[Now+1]!=j)&&Now) Now=Next[Now]; if(Num[Now+1]==j) Now++; f[i][Now]++; } } /* for(i=0;i<m;i++,puts("")) { for(j=0;j<m;j++) W(f[i][j]); } puts(""); */ return; } int main() { int i,j,k,Sum=0; R(n); R(m); R(Mod); for(i=1;i<=m;i++) { char ch=' '; while(!isdigit(ch)) ch=getchar(); Num[i]=ch-'0'; } Pre_f(); ans[0][0]=1; for(i=0;i<=m-1;i++) power[i][i]=1; memmove(a,f,sizeof a); while(n) { if(n&1) { memset(c,0,sizeof c); for(i=0;i<=m-1;i++) for(j=0;j<=m-1;j++) for(k=0;k<=m-1;k++) { Ad(c[i][j],power[i][k]*a[k][j]%Mod); } memmove(power,c,sizeof power); } memset(c,0,sizeof c); for(i=0;i<=m-1;i++) for(j=0;j<=m-1;j++) for(k=0;k<=m-1;k++) { Ad(c[i][j],a[i][k]*a[k][j]%Mod); } memmove(a,c,sizeof a); n>>=1; } memset(c,0,sizeof c); for(i=0;i<=0;i++) for(j=0;j<=m-1;j++) for(k=0;k<=m-1;k++) { Ad(c[i][j],ans[i][k]*power[k][j]%Mod); } memmove(ans,c,sizeof ans); for(i=0;i<m;i++) Ad(Sum,ans[0][i]); Wl(Sum); return 0; } /* input 4 3 100 111 output 81 input 1000000000 19 9973 1010100110011000001 output 5753 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!