Algoogle

Algorithm for Programming Contest

AOJ 2397 Three-way Branch

Category: AOJ Tag: matrix

Three-way Branch

問題概要


幅W(<=75)で高さH(<=10^18)のグリッドがある.
セルから移動できる先は左下か真下か右下だけ.
途中に侵入不可能なマスがN(<=30)個ある.
グリッドの左上から右下まで移動する方法の数を求めよ.

解法


侵入不可能なマスがない場合,普通に行列累乗してやればよい.
なので侵入不可能なマスがある行まで行列累乗して,その行で侵入不可能なマスの場合の数を0にしてまた行列累乗するというのを繰り返す.

コード


(2397.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <bits/stdc++.h>
using namespace std;
#define repi(i,a,b) for(int i = (int)(a); i < (int)(b); i++)
#define rep(i,n) repi(i,0,n)

const long long mod = 1000000009;

typedef long long number;
typedef vector<number> arr;
typedef vector<arr> mat;

arr mul(const mat &A, const arr &x) {
    arr y(A.size());
    for(int i = 0; i < (int)A.size(); ++i)
        for(int j = 0; j < (int)A[0].size(); ++j)
            (y[i] += 1LL * A[i][j] * x[j]) %= mod;
    return y;
}

mat mul(const mat &A, const mat &B) {
    mat C(A.size(), arr(B[0].size()));
    for(int i = 0; i < (int)C.size(); ++i)
        for(int j = 0; j < (int)C[i].size(); ++j)
            for(int k = 0; k < (int)A[i].size(); ++k)
                (C[i][j] += 1LL * A[i][k] * B[k][j] % mod) %= mod;
    return C;
}

mat identity(int n) {
    mat A(n, arr(n));
    for (int i = 0; i < n; i++)
        A[i][i] = 1;
    return A;
}

mat pow(const mat &A, long long e) {
    return e == 0 ? identity(A.size())  :
        e % 2 == 0 ? pow(mul(A, A), e/2) :
        mul(A, pow(A, e-1));
}

long long w, h, n;
map<long long, vector<int>> vs;

long long solve() {
    h--;
    arr as(w,0);
    mat A;
    A.assign(w,arr(w,0));
    rep(i,w) repi(j,max(0,i-1),min((int)w,i+2)) A[i][j] = 1;
    as[0] = 1;
    long long ch = 0;
    for(auto &hs: vs) {
        long long nh = hs.first;
        if(nh <= ch) continue;
        as = mul(pow(A,nh-ch), as);
        for(auto &e: hs.second) as[e] = 0;
        ch = nh;
    }
    as = mul(pow(A,h-ch),as);
    return as[w-1];
}

bool input() {
    cin >> w >> h >> n;
    vs.clear();
    long long x, y;
    rep(i,n) {
        cin >> x >> y;
        vs[y-1].push_back(x-1);
    }
    return w or h or n;
}

int main() {
    for(int i = 1; input(); i++) cout << "Case " << i << ": " << solve() << endl;
    return 0;
}

Comments