[BZOJ2004][Hnoi2010]Bus 公交线路

[BZOJ2004][Hnoi2010]Bus 公交线路

试题描述

小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 2 4

输出示例

81

数据规模及约定

P<=10 , K <=8

题解

状压 dp + 矩阵乘法。

话说最近还想自己出一道这两种算法结合在一起的题呢。。。

这题目标状态只需要让 K 辆车分别在最后 K 个位置即可,顺序没有规定。所以这题设计状态时也不用考虑顺序。我们令 f(i, S) 表示前 i-1 个都已经经过了,第 i 到第 i + P - 1 个位置的状态为 S 的方案数,这里的“方案”可以理解成 K 个公交车所在位置的集合,也可以理解成覆盖过的位置的集合,这两个理解是等价的(原因在于我们转移时只考虑最靠左的公交车移到哪,与此同时 i 要加 1,即我们关注的位置区间向右错一格,所以一个公交车移开之后变成“没有公交车”状态的位置不会出现在状态中)。

由于 n 很大,而每次转移都是形如 f(i, S) -> f(i+1, tS),所以可以构造转移矩阵做矩阵快速幂。

注意最靠前那一位必须是 1,这样可以省掉一部分状态。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxS 1024
#define maxs 260
#define MOD 30031

struct Matrix {
	int n, m, A[maxs][maxs];
	
	Matrix() { memset(A, 0, sizeof(A)); }
	Matrix(int _, int __): n(_), m(__) { memset(A, 0, sizeof(A)); }
	
	Matrix operator * (const Matrix& t) const {
		Matrix ans(n, t.m);
		for(int i = 1; i <= ans.n; i++)
			for(int j = 1; j <= ans.m; j++)
				for(int k = 1; k <= m; k++)
					ans.A[i][j] = (ans.A[i][j] + A[i][k] * t.A[k][j]) % MOD;
		return ans;
	}
	Matrix operator *= (const Matrix& t) {
		*this = *this * t;
		return *this;
	}
} base, tr;

Matrix Pow(Matrix a, int b) {
	Matrix ans = a, t = a; b--;
	while(b) {
		if(b & 1) ans *= t;
		t *= t; b >>= 1;
	}
	return ans;
}

int id[maxS], cnts;

int main() {
	int n = read(), K = read(), P = read();
	
	int all = (1 << P) - 1;
	for(int S = 0; S <= all; S++) {
		int cnt = 0;
		for(int j = 0; j < P; j++) cnt += S >> j & 1;
		if(cnt == K && (S & 1)) id[S] = ++cnts;
	}
	tr = Matrix(cnts, cnts);
	for(int S = 0; S <= all; S++) if(id[S]) {
		int tS = S >> 1;
		for(int j = 0; j < P; j++) if(!(tS >> j & 1) && id[tS|(1<<j)]) tr.A[id[tS|(1<<j)]][id[S]]++;
	}
	base = Matrix(cnts, 1);
	base.A[1][1] = 1;
	
	base = Pow(tr, n - K) * base;
	printf("%d\n", base.A[1][1]);
	
	return 0;
}

 

posted @ 2017-06-03 23:38  xjr01  阅读(440)  评论(0编辑  收藏  举报