[BZOJ2004][Hnoi2010]Bus 公交线路

[Hnoi2010]Bus 公交线路

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

Solution
首先在任意一个长度为p的区间里每个公交车都至少要停一次,因此对于一个站台i来说,我们只需要考虑它往后长度为p的区间的公交车停站状态(1/0表示停/不停),每个状态都恰好有k个1
为了保证每个站都有车停,我们不考虑最左位为0的状态,强制令最左位为1,即i这个站有车停(否则转移时我们将允许某一站被跨越)
进而我们又发现对于当前某一个状态S,如果我们随意移动公交车(移动过程始终合法),采取同一种方案到达某一个状态S’时该方案会被重复计数(比如S到S’需要1号公交车前移3,2号公交车前移4,如果随意移动,无论两个前移谁先谁后,最后都会到达S’,而这是同一种方案,因为每一种公交车经过的站是一样的,但在S’被计数了两次),因此我们每次只移动第i个车站的车,即最落后的一辆车(上面我们也已经强制这里有车了)
至于状态S能不能转移到S’,只要两个状态只有一个位置不一样就行了(这个不一样的是第i辆车移动之后造成的结果)
进而我们又发现对于不同的i状态S与状态S’的转移矩阵都一样,因此我们可以矩阵乘法加速

Code

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define MS(_) memset(_, 0, sizeof(_))
#define PB push_back
#define MP make_pair
#define debug(...) fprintf(stderr, __VA_ARGS__);
typedef long long ll;
typedef pair<int, int> PII;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch))  { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
}

const int N = 210;
const int MAXP = 30;
const ll MOD = 30031;
struct Matrix
{
    ll m[N][N];
    Matrix() { MS(m); }
}ans, t;
int bin[MAXP], n, K, P, cnt = 0, S[N];

Matrix operator *(Matrix a, Matrix b){ Matrix c;
    rep(i,1,cnt)rep(j,1,cnt)rep(k,1,cnt) c.m[i][j] += a.m[i][k]*b.m[k][j];
    rep(i,1,cnt)rep(j,1,cnt) c.m[i][j] %= MOD;
    return c;
}
Matrix operator ^(Matrix a, int b){ Matrix res;
    rep(i, 1, cnt) res.m[i][i] = 1;
    for (; b; b>>=1, a=a*a) if (b&1) res=res*a;
    return res;
}
inline void dfs(int dep, int state, int last){
    if (dep > K){ S[++cnt] = state; return; }
    per(i, last-1, 1) dfs(dep+1, state+bin[i-1], i);
}   
inline void build_matrix(){
    rep(i, 1, cnt) rep(j, 1, cnt){
        int x = (S[i]<<1)^bin[P]^S[j];
        if (__builtin_popcount(x) == 1) t.m[i][j] = 1;
    }
}
int main(){
    bin[0] = 1; rep(i, 1, 20) bin[i] = bin[i-1]<<1;
    read(n); read(K); read(P);

    dfs(2, bin[P-1], P);
    build_matrix();

    ans.m[1][1] = 1;
    Matrix T = t^(n-K);
    ans = ans * T;
    printf("%d\n", ans.m[1][1]);

    return 0;   
}

你可能感兴趣的:(状压dp)