たこし++の備忘録

競技プログラミングの備忘録

Google Code Jam 2016 Round1C : B. Slides!

問題

Dashboard - Round 1C 2016 - Google Code Jam

問題概要

建物が1からBまで番号付けされており、建物同士を有効エッジで繋いでいく。
1からBまでのパスの総数がMになるようにエッジを設置できるかどうかを判定し、設置できる場合は具体的なエッジの張り方をどれでも良いので1つ出力せよ。
1からある建物に行けなかったり、使われないエッジがあっても良いものとする。
 B \le 50 \,\, M \le 10^{18}

解法

無限ループができるとパスの総数も無限になってしまうので、作成されるグラフはDAGである必要がある。したがって、表は左半分だけor右半分だけを埋めれば良い。どちらでも良いですが、この記事は右半分だけを埋めます。
下図のように、1とB以外の建物から、自分より大きな番号の建物に向かってのみエッジを張るようにすると、1から各建物に向かってエッジを貼ると、パスが赤字の分だけ増えることがわかる

f:id:t-pro-6q:20160508234649p:plain
建物の番号をnとすると、赤字は 2^{B-(n+1)}の関係にあることがわかるので、後はMを2進数表記で見た時に立っているビットを見て建物1からどの建物にエッジを貼ればよいかを探索すれば解ける。
注意点として、建物1から直接建物Bにエッジを貼ると先ほどの赤字の法則とは全く関係なく1だけパスを増加させることができるので、先にこのエッジを張ったほうがバグを埋め込みにくくて良いです(?)

コード

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <fstream>
#include <queue>
#include <complex>
  
#define INF 100000000
#define YJ 1145141919
#define INF_INT_MAX 2147483647
#define INF_LL_MAX 9223372036854775807
#define INF_LL 9223372036854775
#define EPS 1e-10
#define Pi acos(-1)
#define LL long long
#define ULL unsigned long long
#define LD long double 

using namespace std;

#define MAX_T 101
#define MAX_B 55

int TEST_CASE;
LL B, M;

int Edge[MAX_B][MAX_B];

void init(){
  memset(Edge, 0, sizeof(Edge));
}

void input(){
  init();
  cin >> B >> M;
}

int main(){

  cin >> TEST_CASE;

  for(int testCase = 0; testCase < TEST_CASE; testCase++){
    
    input();

    //先にエッジを全部張っちゃう
    for(int i = 1; i < B; i++){
      for(int j = i+1; j < B; j++){
	Edge[i][j] = 1;
      }
    }

    //1->Bにエッジを張る
    M--;
    Edge[0][B-1] = 1;

    //どの建物にエッジを貼ればよいかを探索
    for(int i = B-2; 1 <= i; i--){
      if(M&1){
	Edge[0][i] = 1;
	M--;
      }
      if(1 < i)
	M >>= 1;
    }
    
    if(M == 0){
      printf("Case #%d: POSSIBLE\n", testCase+1);
      for(int i = 0; i < B; i++){
	for(int j = 0; j < B; j++){
	  cout << Edge[i][j];
	}
	cout << endl;
      }
    }
    else{
      printf("Case #%d: IMPOSSIBLE\n", testCase+1);
    }

  }

  return 0;

}