P1357 花园
花圃只有两种
m最大为5
可以把C形的花圃看成 1 ,其他的看成 0
每m个花圃看成一个状态,只有 2^5 种状态
显然状态可以互相转移
比如说第 1~5 个花圃为一个状态
它可以转移到第 2~6 个花圃的一个状态
那筛一下可以转移的状态,然后跑DP就可以了
设 f [ i ] [ j ] 表示状态为 j,此状态的开头的位置为 i,那么
f [ i ] [ j ] += f [ i-1 ] [ k ](k可以转移到 j)
但是因为花圃是环形,所以枚举开始的状态 j,然后 i 要从 1 到 n(而不是从 1 到 n-m),把 f [ n ] [ j ] 加到答案里
那么枚举开头的每个状态
把开头的每个状态分别加起来就行了
这样有 80 分
因为每个转移都是从前面一个状态转过来的
所以考虑矩阵优化
那么初始状态矩阵 A[ i ][ i ] = 1(表示以第 i 种状态开始,刚开始只有 i 有一种方案)
注意不是A[1][ i ] = 1,因为每种初始状态要分开讨论
构造一个 2^m * 2^m 的转移矩阵P:
如果 i 可以转移到 j
那么显然 P[ j ][ i ] = 1;
然后就可以跑过了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; const ll mo=1000000007; ll n,m,K,M; struct matrix { ll a[100][100]; matrix() { memset(a,0,sizeof(a)); } matrix operator * (matrix &tmp){ matrix c; for(int i=1;i<(1<<m);i++) for(int j=1;j<(1<<m);j++) for(int k=1;k<(1<<m);k++) c.a[i][j]=(c.a[i][j]+(a[i][k]*tmp.a[k][j])%mo)%mo; return c; } }p; int t[100],cnt; inline void pre() { for(int i=0;i<M;i++) { int num=0; for(int j=0;j<m;j++) if( i&(1<<j) ) num++; if(num<=K) t[++cnt]=i; } for(int i=1;i<=cnt;i++) for(int j=1;j<=cnt;j++) { bool flag=1; int x=t[i],y=t[j]>>1; for(int k=0;k<m-1;k++) { if( (x&1) != (y&1) ) { flag=0; break; } x>>=1; y>>=1; } if(flag) p.a[j][i]=1; } }//预处理 inline matrix ksm(matrix x,ll y) { matrix res; for(int i=0;i<(1<<m);i++) res.a[i][i]=1; while(y) { if(y&1) res=res*x; x=x*x; y>>=1; } return res; } int main() { cin>>n>>m>>K; M=(1<<m); pre(); p=ksm(p,n); ll ans=0; for(int i=1;i<=cnt;i++) ans=(ans+p.a[i][i])%mo; cout<<ans; return 0; }
代码还是比较好写的