您的位置:首页 > 其它

【bzoj2004】[Hnoi2010]Bus 公交线路 状压dp+矩阵乘法

2017-12-19 19:33 323 查看
题目描述

小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km。 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计线路:
1.设共K辆公交车,则1到K号站作为始发站,N-K+1到N号台作为终点站。
2.每个车站必须被一辆且仅一辆公交车经过(始发站和终点站也算被经过)。
3.公交车只能从编号较小的站台驶往编号较大的站台。
4.一辆公交车经过的相邻两个
站台间距离不得超过Pkm。 在最终设计线路之前,小Z想知道有多少种满足要求的方案。由于答案可能很大,你只需求出答案对30031取模的结果。
输入

仅一行包含三个正整数N K P,分别表示公交车站数,公交车数,相邻站台的距离限制。
N<=10^9,1<P<=10,K<N,1<K<=P
输出

仅包含一个整数,表示满足要求的方案数对30031取模的结果。

样例输入

样例一:10 3 3

样例二:5 2 3

样例三:10 2 4

样例输出

1

3

81

题解

状压dp+矩阵乘法

很容易想到以当前枚举位置下每辆车最后的出现位置为状态,即 $f[i][j]$ 表示 $i$ 个位置,每辆车最后的出现位置状态为 $j$ 的方案数。

那么考虑转移:如果某个车的位置是从左到右 $p$ 个中的第 $1$ 个,则必须移动这辆车;否则可以移动任意一辆车。

但是这样状态难以存下($A_p^k$)

仔细思考可以发现,每辆车都是一样的,因此没有顺序之分,状态数从排列变为了组合($C_p^k$),最多只有 $C_{10}^5=252$ 。

而且进一步可以通过dp的意义得到:状态中一定有一辆车在最后一个位置。因此最多只有 $C_9^4=126$ 。

按照这个思路容易想到矩阵乘法。

设二进制状态表示从左到右的 $p$ 个位置中,哪些位置是 $k$ 辆车的最后出现的位置。

那么初始状态和结束状态都可以看成前 $p-k$ 个位置为空,后 $k$ 个位置全有车。显然需要转移 $n-k$ 次。

因此首先预处理状态和转移,然后直接求矩阵的 $n-k$ 次方计算即可。

时间复杂度 $O((C_{p-1}^{k-1})^3\log n)$

#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 30031
using namespace std;
int c[1030] , w[1030] , v[130] , n;
struct data
{
int v[130][130];
data() {memset(v , 0 , sizeof(v));}
int *operator[](int a) {return v[a];}
data operator*(data &a)
{
data ans;
int i , j , k;
for(i = 1 ; i <= n ; i ++ )
for(j = 1 ; j <= n ; j ++ )
for(k = 1 ; k <= n ; k ++ )
ans[i][j] = (ans[i][j] + v[i][k] * a[k][j]) % mod;
return ans;
}
}A;
data pow(data x , int y)
{
data ans;
int i;
for(i = 1 ; i <= n ; i ++ ) ans[i][i] = 1;
while(y)
{
if(y & 1) ans = ans * x;
x = x * x , y >>= 1;
}
return ans;
}
int main()
{
int m , k , p , i , j;
scanf("%d%d%d" , &m , &k , &p);
for(i = 1 ; i < (1 << p) ; i ++ )
{
c[i] = c[i - (i & -i)] + 1;
if(c[i] == k && i & (1 << (p - 1))) w[i] = ++n , v
= i;
}
for(i = 1 ; i <= n ; i ++ )
{
if(v[i] & 1) A[i][w[(1 << (p - 1)) | (v[i] >> 1)]] = 1;
else
for(j = 0 ; j < p ; j ++ )
if(v[i] & (1 << j))
A[i][w[(1 << (p - 1)) | ((v[i] ^ (1 << j)) >> 1)]] = 1;
}
A = pow(A , m - k);
i = w[(1 << p) - (1 << (p - k))];
printf("%d\n" , A[i][i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: