UOJ#266. 【清华集训2016】Alice和Bob又在玩游戏 博弈,DSU on Tree,Trie
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ266.html
题解
首先我们可以直接暴力 O(n2) 用 sg 函数来算答案。
对于一个树就是枚举一下从根出发到哪一个节点为止的路径被删掉了,剩下所有的子树的sg值xor起来,对于每一个路径后的答案取一个 mex 。
我们考虑快速的做这个过程。
直接写个 Trie 再 DSU on tree 就好了,只要支持查询 mex 和整棵 trie 对某一个值 xor 这两种操作就好了。
时间复杂度 O(nlog2n) 。
P.S. 可以用线段树合并优化复杂度。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #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\n",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; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!