grundy数を計算する。
木構造にして、赤い点を打った頂点より上を全て削除する。
残った木でも排他的論理和。
const int N = 50 + 5;
bool g[N][N];
bool h[N][N];
int p[N];
const int M = N - 1;
int memo[N];
int grundy(int curr)
{
int& ret = memo[curr];
if (ret != -1) return ret;
set<int> s;
for (int i = 0; i < N; ++i) {
if (g[curr][i]) {
int n = i;
set<int> vis;
vis.insert(n);
while (n != p[n]) vis.insert(n = p[n]);
int b = 0;
for (int next = 0; next < N; ++next) {
if (g[curr][next] && !vis.count(next) && vis.count(p[next])) {
b ^= grundy(next);
}
}
s.insert(b);
}
}
int res = 0;
while (s.count(res)) ++res;
return ret = res;
}
bool contain(int x1, int y1, int r1, int x2, int y2, int r2)
{
unless (r1 > r2) return false;
int x = x1 - x2;
int y = y1 - y2;
int r = r1 - r2;
return x * x + y * y < r * r;
}
class CirclesGame {
public:
string whoCanWin(vector <int> x, vector <int> y, vector <int> r)
{
const string A = "Alice";
const string B = "Bob";
const int size = x.size();
fill(&g[0][0], &g[N - 1][N - 1] + 1, false);
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
if (i != j) g[i][j] = contain(x[i], y[i], r[i], x[j], y[j], r[j]);
}
}
for (int i = 0; i < size; ++i) {
g[M][i] = true;
}
copy(&g[0][0], &g[N - 1][N - 1] + 1, &h[0][0]);
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
for (int k = 0; k < N; ++k) {
if (h[i][j] && g[i][k] && g[k][j]) h[i][j] = false;
}
}
}
for (int i = 0; i < size; ++i) {
g[i][i] = true;
}
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
if (h[i][j]) p[j] = i;
}
}
p[M] = M;
fill(memo, memo + N, -1);
return grundy(M) ? A : B;
}