読者です 読者をやめる 読者になる 読者になる

GCJ 2016 Round 1C B: Slides!

解法

閉路があると無限ループしてしまうので、求めるグラフはDAGである。よってビル番号の小さい方から大きい方へ移動することにする。

1〜N-1とBのN個の頂点で可能な辺を全て張ってDAGを作ると、1からBへの行き方は 2^{N-2}通りになる。ここにさらに頂点Nを追加する事を考える。Nから移動できる頂点はBのみなので、辺N->Bを張る。

  • 1->Nを張ると、1->N->Bの1通りが増える。
  • 2->Nを張ると、1->2->N->Bの1通りが増える。
  • 3->Nを張ると、1->...->3->N->Bの2通りが増える。
  • 4->Nを張ると、1->...->4->N->Bの4通りが増える。
  • ...

この要領で、求めるべきM通りのうち 2^{N-2}はN個の頂点の部分グラフによって構成し、残ったパターンは頂点Nへの辺の張り方を選んで調節すれば良い。

コード

#include <bits/stdc++.h>
using namespace std;

int64_t fast_pow(int64_t x, int64_t e) {
  int64_t ret = 1;
  int64_t cur = x;
  while (e) {
    if (e & 1) ret *= cur;
    cur *= cur;
    e >>= 1;
  }
  return ret;
}

int64_t dp_check(const vector<vector<bool>> &ans) {
  int N = ans.size();
  vector<int64_t> dp(N, 0);
  dp[0] = 1;

  for (int i = 1; i < N; ++i) {
    for (int j = 0; j < i; ++j) {
      if (ans[j][i]) dp[i] += dp[j];
    }
  }
  return dp[N - 1];
}

vector<vector<bool>> solve(int B, int64_t M) {
  int64_t checkM = M;
  if (fast_pow(2, B - 2) < M) return vector<vector<bool>>();
  vector<vector<bool>> ans(B, vector<bool>(B, false));

  int init = 0;
  while (fast_pow(2, init + 1) <= M) init++;
  for (int i = 0; i <= init; ++i) {
    ans[i][B - 1] = true;
    for (int j = i + 1; j <= init; ++j) {
      ans[i][j] = true;
    }
  }
  M -= fast_pow(2, init);

  int add = init + 1;
  for (int i = init; i > 0; --i) {
    if (M >= fast_pow(2, i - 1)) {
      ans[i][add] = true;
      ans[add][B - 1] = true;
      M -= fast_pow(2, i - 1);
    }
  }
  if (M == 1) ans[0][add] = true;

  if (checkM != dp_check(ans)) {
    cerr << B << " " << checkM << endl;
    for (int i = 0; i < ans.size(); ++i) {
      for (int j = 0; j < ans[i].size(); ++j) {
        if (ans[i][j])
          cerr << 1;
        else
          cerr << 0;
      }
      cerr << endl;
    }
  }
  return ans;
}

int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);

  int T;
  cin >> T;
  for (int testcase = 1; testcase <= T; ++testcase) {
    int B;
    int64_t M;
    cin >> B >> M;

    cout << "Case #" << testcase << ": ";
    vector<vector<bool>> ans = solve(B, M);
    if (ans.size() == 0) {
      cout << "IMPOSSIBLE" << endl;
    } else {
      cout << "POSSIBLE" << endl;
      for (int i = 0; i < ans.size(); ++i) {
        for (int j = 0; j < ans[i].size(); ++j) {
          if (ans[i][j])
            cout << 1;
          else
            cout << 0;
        }
        cout << endl;
      }
    }
  }
}