时间限制:1秒 内存限制:64M
【问题描述】
小 Z 所在的城市有 N 个公交车站,排列在一条长为 N-1 公里的直线上,从左到右依次编号为 1 到 N,相邻公交车站间的距离均为 1 公里。
作为公交车线路的规划者,小 Z 调查了市民的需求,决定按以下规则设计线路:
1.设共有 K 辆公交车,则 1 到 K 号车站作为始发站, N-K+1 到 N 号车站作为终点站。
2.每个车站必须被一辆且仅一辆公交车经停(始发站和终点站也算被经停)。
3.公交车只能从编号较小的车站驶向编号较大的车站。
4.一辆公交车经停的相邻两个车站间的距离不得超过 P 公里。
注意“经停”是指经过并停车, 因经过不一定会停车,故经停与经过是两个不同的概念。
在最终确定线路之前,小 Z 想知道有多少种满足要求的方案。由于答案可能很大,你只需求出答案对 30031 取模的结果。
【输入格式】
只有一行,其中包含用空格隔开的三个正整数N, K,P, 分别表示公交车站数,公交车数, 一辆公交车经停的相邻两个车站间的最大距离。
【输出格式】
仅包含一个整数,表示满足要求的方案数对 30031 取模的结果。
【输入样例】
【样例1】
10 3 3
【样例2】
5 2 3
【样例3】
10 2 4
【输出样例】
【样例1】
1
【样例2】
3
【样例3】
81
【样例解释】
样例一满足要求的方案只有1种,即: (1,4,7,10), (2,5,8), (3,6,9)。
样例二满足要求的方案有3种,即: (1,3,5), (2,4); (1,3,4), (2,5)和(1,4), (2,3,5)。
【数据范围】
40%的数据满足N≤1000。
100%的数据满足1 < N < 10^9, 1 < P ≤1 0, K < N, 1 < K ≤ P
【来源】
bzoj 2004
一道极其坑的题,开始想了好久都没有思路。
先说一下我看到这道题的第一反应吧:
1.p<=10 一定是状态压缩用的。
2.n<=1*10^9 作为一道递推绝对要用矩阵优化。
然后我就卡死了,推了好久才得出的方程。
d[i][A]:前i个站最后p个站的n辆车最后停的位置为A(停了的2进制位就为1)的方案数。(最后一个站必须有车停)
转移的时候我们强制第一个先走,走一步(不然会重)。这样就可以得出哪些情况之间可以转移了。我们就可以矩阵优化了。
答案就是最后n个站都有车停的情况。
代码如下:
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=150;
const int mod=30031;
struct mat
{
int n,m,v[maxn][maxn];
mat()
{
memset(v,0,sizeof(v));
m=n=0;
}
friend mat operator * (mat a,mat b)
{
mat c;
c.m=a.n;c.n=b.m;
for(int i=1;i<=c.m;i++)
for(int j=1;j<=b.m;j++)
for(int k=1;k<=a.m;k++)
c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j])%mod;
return c;
}
}a,B;
int g[maxn],top=0,n,m,p,b[maxn],ans;
int work(int x){
int t=0;
while(x)
{
t++;
x-=(x&(-x));
}
return t;
}
bool check(int i,int j){
int x=g[i],y=g[j];
x=x<<1;
x-=b[p+1];
x=x^y;
return x-(x&(-x))==0;
}
mat qkpow(mat a,int x){
mat t,c;
c.m=c.n=a.m;
for(int i=1;i<=c.m;i++) c.v[i][i]=1;
t=a;
while(x)
{
if (x&1) c=c*t;
x=x>>1;
t=t*t;
}
return c;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d%d%d",&n,&m,&p);
b[1]=1;for(int i=2;i<=p+1;i++) b[i]=b[i-1]<<1;
for(int i=b[p];i<=b[p+1]-1;i++)
if(work(i)==m)
{
g[++top]=i;
if(i==b[p+1]-b[p-m+1]) ans=top;
}
a.m=a.n=top;
for(int i=1;i<=top;i++)
for(int j=1;j<=top;j++)
if(check(i,j)) a.v[j][i]=1;
B.n=top,B.m=1;
B.v[ans][1]=1;
B=qkpow(a,n-m)*B;
printf("%d\n",B.v[ans][1]);
return 0;
}