1. 程式人生 > >[HNOI 2010]Bus 公交線路

[HNOI 2010]Bus 公交線路

pro lowbit gpo online 容易 矩陣 公交 沒有 c++

Description

題庫鏈接

\(N\) 個車站, \(K\) 條公交線路。第 \(1\)\(K\) 站是這 \(K\) 線路的起點站。第 \(N-K+1\)\(N\) 是終點站。車只會從編號小的車站駛向編號大的車站。

要求每個車站恰好只屬於一個線路,而且同一個線路相臨兩站距離不得大於 \(P\) 。求有多少種安排方法。輸出答案對 \(30031\) 取余數。

\(1\leq N\leq 10^9,1<K\leq P\leq 10,K<N\)

Solution

不妨拋開前 \(K\) 個車站不看。

我們發現,只要每連續的 \(P\) 個站中,都出現了所有 \(K\) 種公交車,方案就是合法的。

證明:
如果方案不合法,必有一線路有相鄰站距離大於 \(P\) ,即這連續 \(P\) 個站中缺少一種公交車。根據逆否命題等價,得證。

由於沒有線路車站數的限制, \(P\) 又不大,容易想到狀態壓縮動態規劃:

\(F_{i,S}\) 表示:前 \(i\) 位已經確定完畢,不同公交車最後經停站距 \(i+1\) 的位置的狀態為 \(S\) ,此時的方案總數。

由於公交車是無差別的, \(S\) 實際上是 \(K\) 個不同整數的集合。每個元素都是 \(1\)\(P\) 的數。

更進一步,集合 \(S\) 中一定有一個元素 \(1\) ,其余的都是 \(2\)\(P\)

所以最大只有 \(C_{P-1}^{K-1}\)

個狀態即 \(C_9^5=126\) 這樣一來我們容易想到用矩陣乘法來優化遞推。

Code

//It is made by Awson on 2018.3.12
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
#define writeln(x) (write(x), putchar('\n')) #define lowbit(x) ((x)&(-(x))) using namespace std; const int yzh = 30031, SIZE = (1<<10); void read(int &x) { char ch; bool flag = 0; for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar()); for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar()); x *= 1-2*flag; } void print(int x) {if (x > 9) print(x/10); putchar(x%10+48); } void write(int x) {if (x < 0) putchar('-'); print(Abs(x)); } int n, k, p, bin[20], cnt, sta[130], fin, mp[1050]; struct mat { int a[130][130]; mat() {memset(a, 0, sizeof(a)); } mat(int _a[130][130]) {for (int i = 1; i <= 126; i++) for (int j = 1; j <= 126; j++) a[i][j] = _a[i][j]; } mat operator * (const mat &b) const { mat ans; for (int i = 1; i <= cnt; i++) for (int j = 1; j <= cnt; j++) for (int k = 1; k <= cnt ; k++) ans.a[i][j] = (ans.a[i][j]+1ll*a[i][k]*b.a[k][j]%yzh)%yzh; return ans; } }S, T; mat quick_pow(mat a, int b) { mat ans = a; b--; while (b) { if (b&1) ans = ans*a; a = a*a, b >>= 1; } return ans; } int bitcount(int x) {int ans = 0; while (x) x -= lowbit(x), ++ans; return ans; } void work() { read(n), read(k), read(p); bin[0] = 1; for (int i = 1; i < 20; i++) bin[i] = (bin[i-1]<<1); for (int i = 0; i < bin[p-1]; i++) if (bitcount(i) == k-1) sta[++cnt] = (i<<1|1), mp[i<<1|1] = cnt; for (int i = 1; i <= cnt; i++) { if (sta[i] == bin[k]-1) S.a[i][i] = 1, fin = i; int t = sta[i]; if (t&bin[p-1]) T.a[i][mp[(t-bin[p-1])<<1|1]] = 1; else for (int x = t; x; x -= lowbit(x)) T.a[i][mp[(t-lowbit(x))<<1|1]] = 1; } S = S*quick_pow(T, n-k); writeln(S.a[fin][fin]); } int main() { work(); return 0; }

[HNOI 2010]Bus 公交線路