题目链接
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]的车中, S S S中有 1 1 1的位置都安排有车停,并且恰好只有 k k k个位置安排了(就是 k k k辆车安排到的最后一个站,按照定义,显然 k k k辆车安排的终点站必定在 [ i − p + 1 , i ] [i-p+1,i] [i−p+1,i]内),其他的都没有安排。为了防止重复统计,钦定 S S S的最高位( i − p + 1 i-p+1 i−p+1)必定为 1 1 1。
转移的话就是,如果 f [ i ] [ S ] f[i][S] f[i][S]与 f [ i − 1 ] [ T ] f[i-1][T] f[i−1][T], T T T中的所有 1 1 1与 S S S中的所有 1 1 1能对应上,那么 f [ i ] [ S ] f[i][S] f[i][S]就可以从 f [ i − 1 ] [ T ] f[i-1][T] f[i−1][T]转移过来。
由于状态总数为 ( n n / 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;
}