一开始算出来了每个点的贡献,最后还要O(n)求和,而且递推式还带求和号(而且竟然还是倒着推的,不可矩乘优化
如果正着写:$g[i]= sum_{j=1}^{i-m-1}g[j]$,i每后移一个只增加一个数,去掉求和号:$g[i]=g[i-1]+g[i-m-1]$
答案为g[i]的和+1(全空的情况
然而答案的递推式竟然是:$f[i]=f[i-1]+f[max(i-m-1,0)](f[0]=1)$,(特判$f[1]=2$,
几乎一样啊,只有它会从0转移不同....其实也好理解,如果这个点不染色那么和前面一样$f[i-1]$,如果染色那么前面m个都不会被染色,答案加上一个$f[i-m-1]$,如果$i<=m+1$那么染色方案只会加1,因为只能选一个点染(也就是这段$f[i]=i+1$
这样矩乘转移矩阵就显然了
不开longlong见祖宗
#include<bits/stdc++.h> #define ll long long using namespace std; const int mod=1e9+7; ll n,m; struct mat{ ll a[20][20]; void clear(){ memset(a,0,sizeof(a)); } mat operator *(const mat&t)const{ mat ans;ans.clear(); for(int i=1;i<=m+1;i++) for(int j=1;j<=m+1;j++) for(int k=1;k<=m+1;k++) (ans.a[i][j]+=a[i][k]*t.a[k][j])%=mod; return ans; } }ans,p; mat operator ^(mat x,ll b){ mat ans;ans.clear();for(int i=1;i<=m+1;i++)ans.a[i][i]=1; while(b){ if(b&1)ans=ans*x; x=x*x; b>>=1; } return ans; } int main(){ scanf("%lld%d",&n,&m); for(int i=1;i<=m+1;i++)ans.a[1][m+2-i]=i+1; p.a[1][1]=1; p.a[m+1][1]=1; for(int i=1;i<=m;i++)p.a[i][i+1]=1; if(n<=m+1){ printf("%d",ans.a[1][m+2-n]);return 0; } p=p^(n-m-1);ans=ans*p; printf("%lld",ans.a[1][1]); }