题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=2004
题解
状压dp,记f[i][S]f[i][S]f[i][S]表示[1,i−p][1,i-p][1,i−p]的车都被安排好了,而[i−p+1,i][i-p+1,i][i−p+1,i]的车中,SSS中有111的位置都安排有车停,并且恰好只有kkk个位置安排了(就是kkk辆车安排到的最后一个站,按照定义,显然kkk辆车安排的终点站必定在[i−p+1,i][i-p+1,i][i−p+1,i]内),其他的都没有安排。为了防止重复统计,钦定SSS的最高位(i−p+1i-p+1i−p+1)必定为111。
转移的话就是,如果f[i][S]f[i][S]f[i][S]与f[i−1][T]f[i-1][T]f[i−1][T],TTT中的所有111与SSS中的所有111能对应上,那么f[i][S]f[i][S]f[i][S]就可以从f[i−1][T]f[i-1][T]f[i−1][T]转移过来。
由于状态总数为(nn/2)\binom{n}{n/2}(n/2n)(最大是126),因此可以用矩阵乘法加速转移。
代码
#include <cstdio>
#include <cstring>int read()
{int x=0,f=1;char ch=getchar();while((ch<'0')||(ch>'9')){if(ch=='-'){f=-f;}ch=getchar();}while((ch>='0')&&(ch<='9')){x=x*10+ch-'0';ch=getchar();}return x*f;
}const int maxm=126;
const int mod=30031;struct matrix
{int n,m,a[maxm+2][maxm+2];matrix operator *(const matrix &other) const{matrix res;res.n=n;res.m=other.m;memset(res.a,0,sizeof res.a);for(int i=1; i<=n; ++i){for(int j=1; j<=other.m; ++j){for(int k=1; k<=m; ++k){res.a[i][j]=(res.a[i][j]+1ll*a[i][k]*other.a[k][j])%mod;}}}return res;}
};int stand[maxm+2],n,p,k,tot;
matrix start,trans,ans;matrix quickpow(matrix res,matrix a,int b)
{while(b){if(b&1){res=res*a;}a=a*a;b>>=1;}return res;
}int main()
{n=read();p=read();k=read();for(int i=1<<(k-1); i<1<<k; ++i){int cnt=0;for(int j=0; j<k; ++j){if(i&(1<<j)){++cnt;}}if(cnt==p){stand[++tot]=i;}}start.n=1;start.m=trans.n=trans.m=tot;for(int i=1; i<=tot; ++i){for(int j=1; j<=tot; ++j){int r=(stand[i]<<1)&((1<<k)-1),q=stand[j];for(int s=0; s<k; ++s){if(((r&(1<<s))==0)&&((r|(1<<s))==q)){trans.a[j][i]=1;}}}}start.a[1][tot]=1;ans=quickpow(start,trans,n-p);printf("%d\n",ans.a[1][tot]);return 0;
}