感覺是道好題,但我用了比較久的時間才賀出來
觀察 \(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;
}