Description
Solution
首先每个连通块之间是独立的,也就是说算出每个连通块的(sg)值异或起来就行。
那么每个连通块单独考虑,进行一次题目中的操作后,会产生一些新的连通块,假设当前节点为(x),它能到达的所有点的(sg)值都已经算出来了,那么如果选择的是(x),下一个状态的(sg)值就是所有能到达的点的(sg)值的异或和;否则就是选一个能到达的点从它的子树中删除一个点,再异或上其他没有选的点的(sg)值,而我们可以开一个集合把每个点的子树里的所有点的(sg)值都记录下来,这样每次就相当于对一个集合里的所有数异或某个数,还要支持两个集合合并,用(Trie)维护即可。
求(mex)只需要记(siz_x)表示(x)这个节点表示的数有没有在集合里出现过。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100000;
const int M = 6000000;
int rt[N + 50], n, m, head[N + 50], num, cnt, sg[N + 50], vis[N + 50], ans;
struct Node
{
int next, to;
} edge[N * 2 + 50];
struct SegmentTree
{
int trie[M + 50][2], siz[M + 50], tag[M + 50];
void Pushdown(int x, int k)
{
if (!tag[x]) return;
if ((tag[x] >> k) & 1) swap(trie[x][0], trie[x][1]);
int c = (tag[x] & ((1 << k)) - 1);
tag[trie[x][0]] ^= c; tag[trie[x][1]] ^= c;
tag[x] = 0;
}
int Merge(int x, int y, int k)
{
if (!x || !y) return x + y;
Pushdown(x, k); Pushdown(y, k);
trie[x][0] = Merge(trie[x][0], trie[y][0], k - 1);
trie[x][1] = Merge(trie[x][1], trie[y][1], k - 1);
return x;
}
int Mex(int x)
{
int now = x, ans = 0;
for (int i = 16; i >= 0; i--)
{
if (siz[trie[now][0]] < (1 << i)) now = trie[now][0];
else ans += (1 << i), now = trie[now][1];
}
return ans;
}
int Newnode()
{
int k = ++cnt;
siz[k] = 1;
tag[k] = 0;
trie[k][0] = trie[k][1] = 0;
return k;
}
void Insert(int &u, int x, int k)
{
if (!u) u = Newnode();
if (k >= 0)
{
Pushdown(u, k);
Insert(trie[u][(x >> k) & 1], x, k - 1);
siz[u] = siz[trie[u][1]] + siz[trie[u][0]];
}
return;
}
} tr;
void Addedge(int u, int v)
{
edge[++num] = (Node){head[u], v};
head[u] = num;
return;
}
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
void Dfs(int x, int fa)
{
vis[x] = 1; int tmp = 0;
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa) continue;
Dfs(v, x); tmp ^= sg[v];
}
for (int i = head[x]; i; i = edge[i].next)
{
int v = edge[i].to;
if (v == fa) continue;
tr.tag[rt[v]] ^= (tmp ^ sg[v]);
rt[x] = tr.Merge(rt[x], rt[v], 16);
}
tr.Insert(rt[x], tmp, 16);
sg[x] = tr.Mex(rt[x]);
return;
}
int main()
{
int t;
Read(t);
while (t--)
{
num = ans = 0;
Read(n); Read(m);
for (int i = 1, a, b; i <= m; i++) Read(a), Read(b), Addedge(a, b), Addedge(b, a);
for (int i = 1; i <= n; i++) if (!vis[i]) cnt = 0, Dfs(i, 0), ans = ans ^ sg[i];
puts(ans ? "Alice" : "Bob");
for (int i = 1; i <= n; i++) vis[i] = sg[i] = rt[i] = head[i] = 0;
}
return 0;
}