试题描述
小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; }