[关键字]:数学 矩阵乘法
[题目大意]:有n个车站,k路车。每个车站只能被一路车停,且同一路车的相邻的两站之间不能超过p,求方案数。
//===============================================================================================================================
[分析]:首先因为同一路车的相邻的两站之间不能超过p,所以除前k个每p车站内都要有所有的公交车。这样可以p个p个的转移,用状态压缩dp,p中第一位必须被一辆车占所以方案数一共有c(9,4)=126个。首先处理出所有的合法状态,将这些状态之间的转移构造成矩阵,然后利用矩阵乘法优化加速。
[代码]:
View Code
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MOD=30031; struct node { int n,m; int dat[131][131]; friend node operator * (node a,node b) { node c; c.n=a.n,c.m=b.m; memset(c.dat,0,sizeof(c.dat)); for (int i=1;i<=a.n;++i) for (int j=1;j<=b.m;++j) for (int k=1;k<=a.m;++k) c.dat[i][j]=(c.dat[i][j]+a.dat[i][k]*b.dat[k][j])%MOD; return c; } }ans,g; int n,k,p,x,y,sum; int a[131],b[1200]; bool Cleck(int x) { int sum=0; while (x) { if (x%2) ++sum; x/=2; } return sum==k; } void Make() { g.n=sum,g.m=sum; for (int i=1;i<=sum;++i) { int z=x&(a[i]<<1); for (int j=1;j<=p;++j) if (!((z>>(j-1))&1) && (z+(1<<(j-1)))>=y) ++g.dat[i][b[z+(1<<(j-1))]]; } } void Init() { scanf("%d%d%d",&n,&k,&p); x=(1<<p)-1,y=1<<(p-1); for (int i=0;i<y;++i) if (Cleck(y+i)) a[++sum]=y+i,b[y+i]=sum; /*for (int i=1;i<=sum;++i) printf("%d\n",a[i]);*/ Make(); /*for (int i=1;i<=sum;++i) { for (int j=1;j<=sum;++j) printf("%d ",g.dat[i][j]); printf("\n"); }*/ } void Solve() { int z=b[((1<<k)-1)<<(p-k)]; //printf("%d\n",z); ans.n=1,ans.m=sum; ans.dat[1][z]=1; n-=k; while (1) { if (n&1) ans=ans*g; n>>=1;if (!n) break; g=g*g; } printf("%d\n",ans.dat[1][z]); } int main() { Init(); Solve(); return 0; }