洛谷P6665 [清华集训2016] Alice 和 Bob 又在玩游戏

Link

Solution

考虑对于子树 \(x\)\(SG(x)\),对其后继状态取 \(mex\)

\(x\) 的后继状态就是删去子树 \(x\) 内任意一个点所得若干游戏的 \(SG\) 的异或和。

假设 \(y\)\(x\) 一个儿子,考虑从 \(y\) 转移到 \(x\)

如果删去 \(x\),那么后继状态就是 \(x\) 的儿子的 \(SG\) 的异或和,

否则如果删去子树 \(y\) 中的点,那么 \(y\) 的后继状态会多出来 \(y\) 的兄弟的 \(SG\) 和子树 \(y\) 删完后的的 \(SG\) 的异或和。

当然要把 \(y\) 的后继状态合并到 \(x\) 中。

可以记录下每个子树需要操作的异或和 \(tag\),在求 \(y\) 的兄弟的 \(SG\) 的异或和时,可以直接用 子树 \(x\) 的异或和 ^ 子树 \(y\) 的异或和。

因此需要支持:集合异或一个数、集合合并、集合插入一个数(删点)、查询 \(mex\)

因为是异或,所以可以用 \(01trie\) 维护。

Code

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

const int N  = 4e6 + 5;
int n, m;
vector <int> G[N];
int rt[N], lc[N], rc[N];	//lc:0 rc:1
int sg[N], vis[N];
int cnt, ans;
int tag[N], siz[N];

void pushup(int x)
{
	siz[x] = siz[lc[x]] + siz[rc[x]];
}

void pushdown(int x, int d)
{
	if(!tag[x]) return;
	if(tag[x] & (1 << d)) swap(lc[x], rc[x]);
	tag[lc[x]] ^= tag[x], tag[rc[x]] ^= tag[x];
	tag[x] = 0;
}

int merge(int x, int y, int d)
{
	if(!x || !y) return x + y;
	pushdown(x, d), pushdown(y, d);
	lc[x] = merge(lc[x], lc[y], d - 1);
	rc[x] = merge(rc[x], rc[y], d - 1);
	if(lc[x] || rc[x]) pushup(x);
	return x;
}

void newnode(int &x)
{
	x = ++cnt;
	lc[x] = rc[x] = tag[x] = 0;
	siz[x] = 1;
}

void ins(int &x, int k, int d)
{
	if(!x) newnode(x);
	if(d == -1) return;
	pushdown(x, d);
	ins((k & (1 << d)) ? rc[x] : lc[x], k, d - 1);
	pushup(x);
}

int query(int x, int d)
{
	if(d == -1) return 0;
	pushdown(x, d);
	if(siz[lc[x]] < (1 << d)) return query(lc[x], d - 1);
	else return query(rc[x], d - 1) | (1 << d);
}

int dfs(int u, int fa)
{
	vis[u] = 1;
	int res = 0;
	for(int i = 0; i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(v == fa) continue;
		res ^= dfs(v, u);
	}
	for(int i = 0; i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(v == fa) continue;
		tag[rt[v]] ^= res ^ sg[v];
		rt[u] = merge(rt[u], rt[v], 16);
	}
	ins(rt[u], res, 16);
	return sg[u] = query(rt[u], 16);
}

int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++)
			vis[i] = sg[i] = rt[i] = 0, G[i].clear();
		for(int i = 1; i <= m; i++)
		{
			int u, v;
			scanf("%d%d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		ans = 0;
		for(int i = 1; i <= n; i++)
			if(!vis[i])
			{
				cnt = 0;
				ans ^= dfs(i, 0);
			}
		puts(!ans ? "Bob" : "Alice");
	}
	return 0;
}
posted @ 2021-08-10 09:31  Acestar  阅读(64)  评论(0编辑  收藏  举报