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;
}