BZOJ2004: [Hnoi2010]Bus 公交线路 状压dp+矩阵乘法
这个状压状态时显然的,但是总状态数有 $\binom{K}{P}$.
好在题目中有一个要求,就是每个格子必须经过一次,所以说我们压缩的长度为 $P$ 的状态中首位必为 1.
那么状态数就减小为 $\binom{K-1}{P-1}$,来一个矩阵乘法就行了.
code:
#include <cstdio> #include <algorithm> #include <cstring> #define ll long long #define mod 30031 #define N 303 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int cnt[1<<12],mark[1<<12],idx[1<<12]; struct M { int m[130][130]; M(int t=0) { memset(m,0,sizeof(m)); for(int i=1;i<130;++i) m[i][i]=t; } int *operator[](int x) { return m[x]; } M operator*(const M b) const { M c(0); for(int i=1;i<130;++i) for(int j=1;j<130;++j) for(int k=1;k<130;++k) (c[i][j]+=(ll)m[i][k]*b.m[k][j]%mod)%=mod; return c; } friend M operator^(M a,int k) { M tmp(1); while(k) { if(k&1) tmp=tmp*a; a=a*a,k>>=1; } return tmp; } }v,A; int main() { // setIO("input"); int n,K,P,cn=0; scanf("%d%d%d",&n,&K,&P); for(int i=1;i<(1<<P);++i) { cnt[i]=cnt[i-(i&(-i))]+1; if(cnt[i]==K&&(i&(1<<(P-1)))) mark[i]=1,idx[i]=++cn; } for(int i=1;i<(1<<P);++i) { if(!mark[i]) continue; if(i&1) v[idx[i]][idx[(i>>1)|(1<<(P-1))]]=1; else { for(int j=0;j<P;++j) if(i&(1<<j)) v[idx[i]][idx[((i^(1<<j))>>1)|(1<<(P-1))]]=1; } } A=v^(n-K); int an=idx[(1<<P)-(1<<(P-K))]; printf("%d\n",A[an][an]); return 0; }