zoukankan      html  css  js  c++  java
  • UOJ #266.【清华集训2016】Alice和Bob又在玩游戏

    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;
    }
    
  • 相关阅读:
    《梦断代码》阅读笔记Ⅰ
    BICEP单元测试计划——四则运算Ⅱ
    软件工程随堂小作业——随机四则运算Ⅱ(C++)
    PSP0级 周活动总结表+时间记录日志+缺陷记录日志 表格模板
    阅读《软件工程—理论方法与实践》第十章心得体会
    阅读《软件工程—理论方法与实践》第九章心得体会
    阅读《软件工程—理论方法与实践》第八章心得体会
    阅读《软件工程—理论方法与实践》第七章心得体会
    阅读《软件工程—理论方法与实践》第六章心得体会
    阅读《软件工程—理论方法与实践》第五章心得体会
  • 原文地址:https://www.cnblogs.com/Tian-Xing-Sakura/p/13786883.html
Copyright © 2011-2022 走看看