mayoko’s diary

プロコンとかいろいろ。

GCJ Round 1C Problem B. Slides!

GCJ はなんか苦手(得意なのもないけど)。

問題

Dashboard - Round 1C 2016 - Google Code Jam

B 個の頂点からなる有向グラフを考える。頂点 1 から頂点 B までの経路の数を M 個にしたい。これを満たすグラフは作れるか。作れるなら 1 つそのようなグラフを示せ。

解法

よくわからないまま解いてしまいました。

閉路を作ると経路の数が∞になるので, 閉路は作らないようにしないといけません。
なので, i 番目の辺は, i+1, i+2, ..., B に対して貼ることにします。

で, 答えを M に合わせたいので, 以下のように頂点 i から辺を貼っていきます。

  • i+1 から順 v に見ていく。
  • dp[i] + (v から B への経路数)が M を超えないとき, i -> v の辺を張り, dp[i] += (v から B への経路数) とする

これでうまくいきました。

本番想定されていた解はおそらく上のようなものではありません。

調べるとわかりますが,
1 -> 2, ..., 1 -> B
2 -> 3, ..., 2 -> B
3 -> 4, ..., 3 -> B
...
(B-1) -> B
に辺を張ると, 1 から B への経路数は 2^(B-2) になります(1, B 以外にどの頂点を通るかが 2^(B-2) あるので)。

これを利用して, 2 から後は i -> j (i < j) の辺はすべて貼っておくことにすると, 1 から貼るべき辺というのは, M の 2^(i-2) の bit が立っているなら M-i+1 に辺を張るようにすれば良さそうです(微妙に index ずれてるかも)。

void solve() {
	int B;
	ll M;
	cin >> B >> M;
	ll maxi = 1ll<<(B-2);
	if (maxi < M) {
		cout << "IMPOSSIBLE" << endl;
		return;
	}
	cout << "POSSIBLE" << endl;
	vector<ll> dp(B);
	vector<vi> ans(B, vi(B));
	dp[B-1] = 1;
	for (int i = B-2; i >= 0; i--) {
		ll rest = M;
		for (int j = i+1; j < B; j++) {
			if (rest >= dp[j]) {
				rest -= dp[j];
				dp[i] += dp[j];
				ans[i][j] = 1;
			}
		}
	}
	for (int i = 0; i < B; i++) {
		for (int j = 0; j < B; j++)
			cout << ans[i][j];
		cout << endl;
	}
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int T;
    cin >> T;
    for (int t = 1; t <= T; t++) {
    	cout << "Case #" << t << ": ";
    	solve();
    }
    return 0;
}