mayoko’s diary

プロコンとかいろいろ。

AOJ 2397 Three-way Branch

問題

Three-way Branch | Aizu Online Judge

W*H のグリッドが与えられる。これらのうち, N 個のセルは通れなくなっている。あるセル(x, y) からは (x-1, y+1) or (x, y+1) or (x+1, y+1) にのみ行ける場合, (0, 0) から (W-1, H-1) までの経路は何通りあるか。

解法

W がまぁまぁ小さくて H が超でかいので行列累乗な感じがします。

障害物は N 個しかないので,

  • 障害物のある行になるまでは行列累乗
  • 障害物ある行では 対応するところの場合の数を 0 にする

というようにやれば O(W^3 log H * N) でできます。…結構ギリギリですが。

typedef long long number;
typedef vector<number> vec;
typedef vector<vec> matrix;

const int MOD = 1e9+9;

// O( n )
matrix Identity(int n) {
    matrix A(n, vec(n));
    for (int i = 0; i < n; ++i) A[i][i] = 1;
    return A;
}
// O( n^3 )
matrix mul(const matrix &A, const matrix &B) {
    matrix C(A.size(), vec(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] += A[i][k] * B[k][j];
                C[i][j] %= MOD;
            }
    return C;
}
// O( n^3 log e )
matrix pow(const matrix &A, ll e) {
    if (e == 0) return Identity(A.size());
    if (e == 1) return A;
    if (e % 2 == 0) {
        matrix tmp = pow(A, e/2);
        return mul(tmp, tmp);
    } else {
        matrix tmp = pow(A, e-1);
        return mul(A, tmp);
    }
}
// O(n)
number tr(const matrix &A) {
    number ans = 0;
    for (int i = 0; i < (int)A.size(); ++i)
        ans += A[i][i];
    return ans;
}
// O( n )
number inner_product(const vec &a, const vec &b) {
    number ans = 0;
    for (int i = 0; i < (int)a.size(); ++i)
        ans += a[i] * b[i];
    return ans;
}
// O( n^2 )
vec mul(const matrix &A, const vec &x) {
    vec y(A.size());
    for (int i = 0; i < (int)A.size(); ++i)
        for (int j = 0; j < (int)A[0].size(); ++j)
            (y[i] += (A[i][j] * x[j] % MOD)) %= MOD;
    return y;
}


const int MAXN = 33;
int W, N;
ll H;

ll solve() {
	vector<pair<ll, int> > P(N);
	for (int i = 0; i < N; i++) {
		cin >> P[i].second >> P[i].first;
		P[i].first--; P[i].second--;
	}
	sort(P.begin(), P.end());
	vec v(W);
	v[0] = 1;
	ll now = 0;
	for (int i = 0; i < N; ) {
		matrix mat(W, vec(W));
		for (int j = 0; j < W; j++) {
			if (j-1 >= 0) mat[j][j-1] = 1;
			mat[j][j] = 1;
			if (j+1 < W) mat[j][j+1] = 1;
		}
		v = mul(pow(mat, P[i].first-now), v);
		now = P[i].first;
		int ni = i;
		while (ni < N && P[i].first == P[ni].first)
			ni++;
		for (int j = i; j < ni; j++)
			v[P[j].second] = 0;
		i = ni;
	}
	matrix mat(W, vec(W));
	for (int i = 0; i < W; i++) {
		if (i-1 >= 0) mat[i][i-1] = 1;
		mat[i][i] = 1;
		if (i+1 < W) mat[i][i+1] = 1;
	}
	v = mul(pow(mat, H-1-now), v);
	return v[W-1];
}

int main() {
	cin.tie(0);
	ios::sync_with_stdio(false);
	for (int t = 1; ; t++) {
		cin >> W >> H >> N;
		if (W == 0) break;
		cout << "Case " << t << ": " << solve() << endl;
	}
	return 0;
}