zoukankan      html  css  js  c++  java
  • UOJ#266. 【清华集训2016】Alice和Bob又在玩游戏 博弈,DSU on Tree,Trie

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ266.html

    题解

    首先我们可以直接暴力 $O(n^2)$ 用 sg 函数来算答案。

    对于一个树就是枚举一下从根出发到哪一个节点为止的路径被删掉了,剩下所有的子树的sg值xor起来,对于每一个路径后的答案取一个 mex 。

    我们考虑快速的做这个过程。

    直接写个 Trie 再 DSU on tree 就好了,只要支持查询 mex 和整棵 trie 对某一个值 xor 这两种操作就好了。

    时间复杂度 $O(nlog ^2n)$ 。

    P.S. 可以用线段树合并优化复杂度。

    代码

    #pragma GCC optimize("Ofast","inline")
    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof (x))
    #define For(i,a,b) for (int i=a;i<=b;i++)
    #define Fod(i,b,a) for (int i=b;i>=a;i--)
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
    #define outval(x) printf(#x" = %d
    ",x)
    #define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
    #define outtag(x) puts("----------"#x"----------")
    #define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);
    						For(_v2,L,R)printf("%d ",a[_v2]);puts("");
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f|=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=100005;
    namespace Trie{
    	const int S=N*20*20;
    	int son[S][2],size[S],c=0;
    	#define ls son[x][0]
    	#define rs son[x][1]
    	void Init(){
    		while (c)
    			clr(son[c]),size[c]=0,c--;
    	}
    	void Ins(int &x,int d,int v,int tag){
    		if (!x)
    			x=++c,size[x]=1;
    		if (d<0)
    			return;
    		Ins(son[x][(v^tag)>>d&1],d-1,v,tag);
    		size[x]=size[ls]+size[rs];
    	}
    	void Get(int x,int d,int v,int tag,vector <int> &vec){
    		if (!x)
    			return;
    		if (d<0)
    			return vec.pb(v);
    		Get(son[x][tag>>d&1],d-1,v,tag,vec);
    		Get(son[x][~tag>>d&1],d-1,v|(1<<d),tag,vec);
    	}
    	int Ask(int x,int d,int v,int tag){
    		if (d<0)
    			return v;
    		if (size[son[x][tag>>d&1]]<(1<<d))
    			return Ask(son[x][tag>>d&1],d-1,v,tag);
    		else
    			return Ask(son[x][~tag>>d&1],d-1,v|(1<<d),tag);
    	}
    	#undef ls
    	#undef rs
    }
    int n,m;
    vector <int> e[N];
    struct trie{
    	int rt,tag;
    }t[N];
    int cnt;
    int size[N],son[N],sg[N],id[N];
    void dfs(int x,int pre){
    	size[x]=1,son[x]=0;
    	for (auto y : e[x])
    		if (y!=pre){
    			dfs(y,x);
    			size[x]+=size[y];
    			if (!son[x]||size[son[x]]<size[y])
    				son[x]=y;
    		}
    }
    vector <int> gid;
    void dfs2(int x,int pre){
    	sg[x]=0;
    	int s=0;
    	for (auto y : e[x])
    		if (y!=pre){
    			dfs2(y,x);
    			s^=sg[y];
    		}
    	if (!son[x])
    		id[x]=++cnt,t[cnt].rt=t[cnt].tag=0;
    	else
    		t[id[x]=id[son[x]]].tag^=s^sg[son[x]];
    	trie &now=t[id[x]];
    	Trie::Ins(now.rt,17,s,now.tag);
    	for (auto y : e[x])
    		if (y!=pre&&y!=son[x]){
    			gid.clear();
    			Trie::Get(t[id[y]].rt,17,0,t[id[y]].tag,gid);
    			for (auto v : gid)
    				Trie::Ins(now.rt,17,v^(s^sg[y]),now.tag);
    		}
    	sg[x]=Trie::Ask(now.rt,17,0,now.tag);
    }
    void Main(){
    	n=read(),m=read();
    	For(i,0,n)
    		e[i].clear(),size[i]=0;
    	For(i,1,m){
    		int x=read(),y=read();
    		e[x].pb(y),e[y].pb(x);
    	}
    	cnt=0;
    	int ans=0;
    	Trie::Init();
    	For(i,1,n)
    		if (!size[i]){
    			dfs(i,0);
    			dfs2(i,0);
    			ans^=sg[i];
    		}
    	puts(ans?"Alice":"Bob");
    }
    int main(){
    	int T=read();
    	while (T--)
    		Main();
    	return 0;
    }
    

      

  • 相关阅读:
    Python中循环引用(import)失败的解决方法
    junit中线程需要注意的问题
    python动态绑定属性和方法
    python输出缓冲区的问题
    使用RateLimiter完成简单的大流量限流,抢购秒杀限流
    guava的限流工具RateLimiter使用
    高性能分布式锁-redisson的使用
    正则表达式
    input 标签鼠标放入输入框补全提示
    Google guava工具类的介绍和使用
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ266.html
Copyright © 2011-2022 走看看