BZOJ 2004 公交线路(状压DP+矩阵快速幂)
注意到每个路线相邻车站的距离不超过K,也就是说我们可以对连续K个车站的状态进行状压。
然后状压DP一下,用矩阵快速幂加速运算即可。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 140 #define MOD 30031 using namespace std; struct Matrix { int num[MAXN][MAXN]; int n,m; //n*m大小矩阵 void setOne(int a,int b) { n=a,m=b; for(int i=1;i<=min(n,m);i++) num[i][i]=1; } Matrix() { memset(num,0,sizeof(num)); } }T,A,one; Matrix operator*(Matrix a,Matrix b) { Matrix c; c.n=a.n,c.m=b.m; for(int i=1;i<=c.n;i++) for(int j=1;j<=c.m;j++) for(int k=1;k<=a.m;k++) c.num[i][j]=(c.num[i][j]+a.num[i][k]*b.num[k][j])%MOD; return c; } Matrix fastPow(Matrix base,int pow) { Matrix ans; ans.setOne(base.n,base.m); while(pow) { if(pow&1) ans=ans*base; base=base*base; pow>>=1; } return ans; } int calc(int x) //计算x的二进制中1的个数 { int sum=0; while(x) { sum++; x-=x&(-x); //x去掉最后一个1 } return sum; } int n,k,p,goal; //goal是目标状态 bool canConvert(int to,int from) //检查状态from能否一步转移到状态to { from=(from-(1<<(p-1)))<<1; //这一步相当于把from向左推了一位,个位用0补齐 int tmp=from^to; //tmp应该只有一个1 if(tmp==(tmp&(-tmp))) return true; //tmp只有一个1,则是合法的 return false; //否则是不合法的 } int status[MAXN],top=0; //保存所有DP过程中可能出现的状态的栈 int main() { scanf("%d%d%d",&n,&k,&p); for(int S=(1<<(p-1));S<(1<<p);S++) //枚举DP状态S,S是合法状态当且仅当S的二进制中1的个数恰好为k { if(calc(S)==k) { status[++top]=S; if(S==(1<<p)-1-((1<<(p-k))-1)) goal=top; //S是最终要达到的状态 } } for(int i=1;i<=top;i++) for(int j=1;j<=top;j++) if(canConvert(status[i],status[j])) T.num[i][j]=1; A.n=A.m=T.n=T.m=top; A.num[1][goal]=1; T=fastPow(T,n-k); A=A*T; printf("%d\n",A.num[1][goal]); return 0; }