バイトの競プロメモ

主に競技プログラミング

AtCoder Grand Contest 031 C - Differ by 1 Bit

C - Differ by 1 Bit

 

Submission #4602092 - AtCoder Grand Contest 031

snukeさんのコードを参考にしました。

方針は解説生放送と同じです。

 

解法

まず遷移は奇数回なので、aとbの立っているbitの数が偶数差ならNO。

それ以外の場合はYES。(証明できないが)

 

nが1~3の時のグラフを描く

 

f:id:baitop:20190317224019p:plain

グラフはn次元超立方体になり、xyz....軸は、変化したbitの桁に対応している。

 

また、n次元のグラフは漸化的に作れる。

n-1次元のグラフを二つ用意して、nビット目の辺で結んでやればいい↓。

 

f:id:baitop:20190317231024p:plain

 

 

今回の解法はその逆操作で、n次元のグラフを二つのn-1次元に分解する事を繰り返して、1次元に帰着させて解く。

↓赤線で分解し、数字を中継地点として部分問題を解いている

f:id:baitop:20190317234044p:plain

f:id:baitop:20190317234049p:plain

f:id:baitop:20190317234345p:plain

f:id:baitop:20190317234130p:plain

 

具体的に分解する物

 

sとtのxorを取ると、sからtへ移動する際に変化する必要があるbitが分かる。

その中のあるビットの軸(上でビットの変化をxyz軸のように対応させた)を切断してグラフを二つに分けると、sとtを分離させることができる。

 

分解する方法

 

今までに分解したbitの集合を持ち、その方向へは動かないようにすれば分解したことになる。

 

Submission #4615397 - AtCoder Grand Contest 031

int n, a, b;
vi res;

//最下位ビット
int lbit(int n) {
return n & -n;
}
//最上位ビット
int hbit(int n) {
n |= (n >> 1);
n |= (n >> 2);
n |= (n >> 4);
n |= (n >> 8);
n |= (n >> 16);
n |= (n >> 32);
return n - (n >> 1);
}
#define bcou __builtin_popcountll

void dfs(int s, int t, int cuts) {
//t以外に行ける場所がない
if (bcou(cuts) == n - 1) {
res.pb(s);
res.pb(t);
return;
}
//s -> t で使うことになる軸の集合
int need = s ^t;
//needに含まれる軸ncutで切断すれば、sとtを分離できる
int ncut = lbit(need);
cuts |= ncut;
//sとtを分離した時、s側で使える軸の集合
int cand = ((1 << n) - 1) ^cuts;
int mid = s ^lbit(cand);
//t側のスタート地点がtになってはいけない
if ((mid ^ ncut) == t) {
mid = s ^ hbit(cand);
}
//二つの部分問題に帰着できた
dfs(s, mid, cuts);
//ncutでsとtを分離したため、mid^ncutでt側に移動できる
dfs(mid ^ ncut, t, cuts);
}

void solve() {
cin >> n >> a >> b;
if (bcou(a ^ b)%2 == 0) {
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
dfs(a, b, 0);
cout << res << endl;
}