此题主要思路是dp,但dp有多种写法。
我们用f[i][j]表示现在已经有i个数字了,而且最后j个数字是不幸数字前j个数字。(保证不包含不吉利序列)
为了防止dp的意义出现歧义,我们让对于同一个状态j要竟可能大;
比如 1212是不幸数字,那么551212是f[6][4];
这样的话我们可以发现f[0][0]=1;
ans=f[n][k] ,k∈[0,m-1] ∪Z(若k=m,那么已经形成一个不吉利序列)
这样的话我们可以从f[0][0]地推;
每局这个状态最后放一个什么数字,然后kmp求j
a[j][k]表示j个不幸之后放k产生的新的j
a[j][k]这个可以用kmp解决
这样可以得到转移方程f[i+1][a[j][k]]=(f[i+1][a[j][k]]+f[i][j])%mo;
意为已经知道f[i][j],最后j个是不吉利数字的前j个,加上一个数字k,最多形成不吉利序列的前a[j][k]个,所以可以通过f[i][j]的状态转移到f[i+1][a[j][k]]的状态。
此题满分需要用矩阵优化dp,但修改以后占比很小,此处不讲,想学习的同学可以上网查题解。
下面是网上使用范围更多的一种转移方程(但没有标程)。
设f[i][j]表示内容同上
f[i][j]的准确含义: f[i][j]表示的每种方案都不含长度大于j且与不吉利数的前缀相同 的后缀 (同上)
否则就会出现:从1到m标号,不吉利数为123124时,f[i][2]计数的方案包含f[i][5]计数的方案 的情况
状态转移:
f[i][j]只能由f[i-1][k]得到,相当于填完第i-1位后,将其不吉利后缀k(长为k的后缀)后面新添一位t,加好后之后这个i位数的 与不吉利数前缀相同的最长后缀长度为j
我们定义a[k][j]就表示有几种方法可以通过加一个数字使长度为k的后缀变为长度为j的后缀(最长后缀),可以用kmp算法预处理出来,
当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][3]的后缀为*****123,f[i-1][2]末尾的*****12不能是**12312(详见定义),所以需要f[i-1][5](***12312)补充
但若不吉利数为123123,那么 f[i][3]=f[i-1][2],因为 f[i][3]末尾的*****123不能是**123123(包含不吉利序列)
i==0时:f[0][0]=1;
这样就可以不重不漏地计数了。
其实这两个题解思路大同小异,打过以后发现代码也很像
90分
#include <bits/stdc++.h> #define N 25 #define M 10005 using namespace std; inline int read(){ int ret=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())ret=ret*10+c-'0'; return ret*f; } int f[2][N],p[N],a[N][N],n,m,mo,ans; char c[N]; inline int dfs(int j,int k){ k+=48; while(~j&&c[j+1]!=k)j=p[j]; return j+1; } int main(){ freopen("lottery.in","r",stdin); freopen("lottery.out","w",stdout); register int ll; scanf("%d%d%d%d",&n,&m,&mo,&ll); scanf("%s",c+1); p[0]=-1;register int j=-1; for(register int i=1;i<=m;p[i++]=++j){ while(~j&&c[i]!=c[j+1])j=p[j]; } for(register int j=0;j<=m-1;j++) for(register int i=0;i<=9;i++) a[j][i]=dfs(j,i); f[0][0]=1; for(register int i=0;i<n;++i){ for(register int j=0;j<m;++j){ for(register int k=0;k<=9;++k){ f[(i+1)&1][a[j][k]]=(f[(i+1)&1][a[j][k]]+f[i&1][j])%mo; } f[i&1][j]=0; } } for(register int i=0;i<m;++i)(ans+=f[n&1][i])%=mo; int s=ans % mo; if (s==0) s+=mo; printf("0."); register int t=1; for (register int i=1;i<ll;i++) { t*=10; printf("%d",t/s); t%=s; } t*=10; int t1=t/s; t%=s; t*=10; if (t/s>=5) t1++; printf("%d\n",t1); return 0; }
100分
#include<bits/stdc++.h> using namespace std; int n,m,mod,s,nxt[21],ll; char c[21]; struct matrix { int f[21][21]; matrix() { memset(f,0,sizeof(f)); } }a; matrix operator*(const matrix &a,const matrix &b) { matrix c; for(register int i=0;i<m;i++) for(register int j=0;j<m;j++) for(register int k=0;k<m;k++) c.f[i][j]=(a.f[i][k]*b.f[k][j]+c.f[i][j]) % mod; return c; } int search(int j,int k) { while (j!=-1 && c[j+1]!=char(k+48)) j=nxt[j]; return j+1; } matrix qpow(matrix now,int x) { if (x==1LL) return now; matrix c=qpow(now,x>>1); if (x & 1) return c*c*now; else return c*c; } int main() { freopen("lottery.in","r",stdin); freopen("lottery.out","w",stdout); scanf("%d%d%d%d",&n,&m,&mod,&ll); scanf("%s",c+1); register int j=-1; nxt[0]=-1; for (register int i=1;i<=m;nxt[i++]=++j) while (j!=-1 && c[i]!=c[j+1]) j=nxt[j];//kmp for (register int j=0;j<m;j++) for(register int i=0;i<=9;i++) a.f[j][search(j,i)]++; a=qpow(a,n); for (register int i=0;i<m;i++) s=(s+a.f[0][i]) % mod; if (s==0) s+=mod; printf("0."); int t=1; for (register int i=1;i<ll;i++) { t*=10; printf("%d",t/s); t%=s; } t*=10; int t1=t/s; t%=s; t*=10; if (t/s>=5) t1++; printf("%d\n",t1); }