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;
}

浙公网安备 33010602011771号