题意
小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距
离均为1km。 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路:
1.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站。
2.每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过)。
3.公交车只能从编号较小的站台驶往编号较大的站台。
4.一辆公交车经过的相邻两个
站台间距离不得超过Pkm。 在最终设计线路之前,小Z想知道有多少种满足要求的方案。由于答案可能很大,你只需求出答案对30031取模的结果。
N<=10^9,1
分析
注意到p其实并不大,就可以考虑状压一波。
设f[i,s]表示从前往后走到了第i个车站,若s的第j位二进制为1,则表示有一辆车最后停靠在车站j。并且第i个车站之前的每一个车站都被车停靠过时的方案数。直接预处理转移矩阵后快速幂优化即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int MOD=30031;
int n,k,p,bin[20],sz,num[2005],cnt[2005];
struct Matrix
{
int a[305][305];
void clear(int n)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]=0;
}
void unit(int n)
{
clear(n);
for (int i=1;i<=n;i++) a[i][i]=1;
}
}a,ans,tmp;
void prework()
{
bin[0]=1;
for (int i=1;i<=p;i++) bin[i]=bin[i-1]*2;
for (int i=1;i<bin[p];i++)
{
int x=i;
while (x) cnt[i]++,x-=x&(-x);
if (cnt[i]==k&&(i&bin[p-1])) num[i]=++sz;
}
for (int i=0;i<bin[p];i++)
if (num[i])
{
if (i&bin[0]) a.a[num[i]][num[(i>>1)^bin[p-1]]]++;
else
{
for (int j=1;j<p;j++)
if (i&bin[j]) a.a[num[i]][num[((i^bin[j])>>1)^bin[p-1]]]++;
}
}
}
void mul1()
{
tmp.clear(sz);
for (int i=1;i<=sz;i++)
for (int k=1;k<=sz;k++)
for (int j=1;j<=sz;j++)
(tmp.a[i][j]+=ans.a[i][k]*a.a[k][j])%=MOD;
ans=tmp;
}
void mul2()
{
tmp.clear(sz);
for (int i=1;i<=sz;i++)
for (int k=1;k<=sz;k++)
for (int j=1;j<=sz;j++)
(tmp.a[i][j]+=a.a[i][k]*a.a[k][j])%=MOD;
a=tmp;
}
void ksm(int y)
{
ans.unit(sz);
while (y)
{
if (y&1) mul1();
mul2();y>>=1;
}
}
int main()
{
scanf("%d%d%d",&n,&k,&p);
prework();
ksm(n-k);
int s=0;
for (int i=0;i<k;i++) s^=bin[p-1-i];
printf("%d",ans.a[num[s]][num[s]]);
return 0;
}