感觉是道好题,但我用了比较久的时间才贺出来
观察 \(m\) 和 \(k\) 很小,而题目只要求相邻 \(m\) 个满足要求 ,显然直接对 \(m\) 个 0 或 1 状压(后文的数字 1 指的是填 C)。设 \(dp[i][j]\) 表示考虑到第 \(i\) 位,当前 \(i\) 到 \(i-m+1\) 的状态,发现当前连续长为 \(m\) 的区间向后推进时,最左边的舍弃,新来的最右边两种情况讨论 DP ,看最右边填 1 是否合法即可。但我如何在二进制下把最左边的删了,在第一个数位前再加进来一个数?我不会弄,但是如果你这个长为 \(m\) 的区间放在整个长 \(n\) 的数列最右端,向左扫描,那我是不是直接左移一位,然后“与”操作就可以了,实现的时候可以正序,最后答案是一样的。
然而是环,显然我们能把 \(1\) 到 \(m\) 复制一遍放最后,然后判断就行,但是我们要要求 \(n+1\) 到 \(n+m\) 和 \(1\) 到 \(m\) 的数要一样,实际上直接答案是 \(dp[n+m][x]\) 就行, \(x\) 为枚举的 \(1\) 到 \(m\) 的数,代码:
#include<bits/stdc++.h>
#define vd void
#define int long long
const int mod=1e9+7;
int gi(){
char c;int x=0,f=0;
while(!isdigit(c=getchar()))f|=(c=='-');
while(isdigit(c))x=(x*10)+(c^48),c=getchar();
return f?-x:x;
}
vd inc(int&a,int b){a=a+b>=mod?a+b-mod:a+b;}
int get1(int a){return __builtin_popcount(a);}
int n,m,K,dp[1025][1024],ans=0;
int solve(int x){
memset(dp,0,sizeof(dp));
dp[m][x]=1;
for(int i=m+1;i<=n+m;i++){
for(int j=0;j<(1<<m);j++){
if(get1(j)>K)continue;
inc(dp[i][j],dp[i-1][j>>1]); //最右边为0
if(get1((j>>1)|(1<<m-1))<=K)inc(dp[i][j],dp[i-1][(j>>1)|(1<<m-1)]); //为1
}
}
return dp[n+m][x];
}
signed main(){
n=gi(),m=gi(),K=gi();
for(int i=0;i<(1<<m);i++)if(get1(i)<=K)inc(ans,solve(i));
printf("%lld\n",ans);
return 0;
}
能够发现, \(dp\) 第一维直接滚掉, \(n\) 很大,而且每个转移都能用矩阵刻画,直接上矩阵快速幂,注意不要像上面一样枚举开始的 \(m\) 个数了,可以直接放到矩阵的对角线上,相乘就可以了,代码:
#include<bits/stdc++.h>
#define vd void
#define int long long
const int mod=1e9+7;
int gi(){
char c;int x=0,f=0;
while(!isdigit(c=getchar()))f|=(c=='-');
while(isdigit(c))x=(x*10)+(c^48),c=getchar();
return f?-x:x;
}
vd inc(int&a,int b){a=a+b>=mod?a+b-mod:a+b;}
int get1(int a){return __builtin_popcount(a);}
int n,m,K,ans=0;
struct mat{
int a[35][35];
mat(){memset(a,0,sizeof(a));}
vd init(){for(int i=0;i<32;i++)a[i][i]=1;}
mat operator*(const mat&T)const{
mat res;
for(int i=0;i<32;i++)
for(int j=0;j<32;j++)
for(int k=0;k<32;k++)res.a[i][j]=(res.a[i][j]+a[i][k]*T.a[k][j]%mod)%mod;
return res;
}
mat operator^(int x)const{
mat res,g;res.init();
for(int i=0;i<32;i++)for(int j=0;j<32;j++)g.a[i][j]=a[i][j]%mod;
while(x){
if(x&1)res=res*g;
g=g*g,x>>=1;
}
return res;
}
};
signed main(){
n=gi(),m=gi(),K=gi();
mat qwq,t;qwq.init();
for(int i=0;i<(1<<m);i++){
if(get1(i)>K)continue;
int j=i>>1;t.a[j][i]=1;j|=(1<<m-1);
if(get1(j)<=K)t.a[j][i]=1;
}
t=t^n,qwq=qwq*t;
for(int i=0;i<(1<<m);i++)inc(ans,qwq.a[i][i]);
printf("%lld\n",ans);
return 0;
}