题面传送门

神仙题。

首先注意到此题的游戏是一个 ICG,故考虑使用 SG 定理解决这个题,显然我们只需对每个连通块计算一遍其 SG 值异或起来检验是否非零即可。注意到我们每删除一个点到根节点的路径后留下的是一些互不影响的子树,并且剩余部分的 SG 值就是剩余子树的 SG 值异或起来,因此我们考虑设 \(sg_u\) 表示 \(u\) 子树部分的 SG 值,我们再实时维护一个 \(t_v\) 表示删除 \(v\) 到当前计算的节点(譬如我们计算 \(sg_u\) 的时候当前计算的节点就是 \(u\))之后,剩余部分的 SG 值的异或和。那么显然 \(sg_u\) 就等于所有 \(u\) 子树内的点 \(v\) 的 \(t_v\) 的 MEX。

因此我们有了 \(n^2\) 的解法,直接暴力枚举 \(u\) 子树内所有点并计算其 MEX。

考虑优化这个过程,当我们计算 \(u\) 子树的 SG 时,我们首先 DFS 一遍 \(u\) 的所有儿子 \(v\) 并计算 \(v\) 子树内所有点的 \(sg_v\) 及 \(t_v\),记 \(S=\operatorname{xor}\limits_{v\in son_u}sg_v\),那么必然有 \(t_u=S\)。而显然对于 \(u\) 的一个儿子 \(v\) 而言,我们在 DFS \(v\) 的时候,所有 \(v\) 子树中的点 \(w\) 本来的 \(t_w\) 肯定是删除 \(w\) 到 \(v\) 路径上的点后,剩余部分 SG 值的异或和,现在根节点由 \(v\) 变成了 \(u\),删除 \(w\) 到 \(u\) 之后的剩余部分与删除 \(w\) 到 \(v\) 之后的剩余部分相比,肯定会多出一部分出来,而这多出的部分又显然是 \(u\) 的所有儿子扣掉 \(v\),它们的 SG 值的异或和即为 \(S\oplus sg_v\),故我们需要将所有 \(v\) 子树中的 \(w\) 的 \(t_w\) 都异或上 \(S\oplus sg_v\)。

可是这样还是没法从本质上优化之前 \(n^2\) 的暴力解法。注意到此题设计异或运算,因此考虑 01-trie,我们对于每个点 \(u\) 维护一个 01-trie,里面存了 \(u\) 子树中所有点的 \(t\) 值,注意到”将 \(v\) 子树中所有点的 \(t\) 值都异或上某个值“这个操作相当于对整棵 trie 打一个 tag,这个显然是可以在常数时间内完成的,而当我们计算 \(sg_u\) 时合并子树信息时,可以像去年省选 D2T2 一样通过类似于线段树合并/堆合并的方式,将所有儿子节点的 01-trie 合并,求一个集合的 MEX 时就在 01-trie 上二分即可。简单来说就是要实现一下四个操作:

  • 对每个儿子节点的 01-trie 整体打一个异或的标记
  • 将儿子节点的 01-trie 合并起来
  • 在当前节点的 01-trie 中插入 \(t_u=\operatorname{xor}\limits_{v\in son_u}sg_v\)
  • 在 01-trie 上二分找到第一个未出现的位置

这样即可将时间复杂度优化到 \(n\log n\)。

const int MAXN=1e5;
const int MAXB=18;
const int MAXP=MAXN*40;
int n,m,hd[MAXN+5],to[MAXN*2+10],nxt[MAXN*2+10],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
struct node{int ch[2],lz,siz;} s[MAXP+5];
int rt[MAXN+5],ncnt=0,sg[MAXN+5],vis[MAXN+5];
void clear(){
memset(hd,0,sizeof(hd));ec=0;
memset(sg,0,sizeof(sg));memset(vis,0,sizeof(vis));
for(int i=1;i<=ncnt;i++) s[i].ch[0]=s[i].ch[1]=s[i].lz=s[i].siz=0;
memset(rt,0,sizeof(rt));ncnt=0;
}
void pushup(int k){s[k].siz=s[s[k].ch[0]].siz+s[s[k].ch[1]].siz;}
void pushdown(int k,int d){
if(s[k].lz){
if(s[k].lz>>d&1) swap(s[k].ch[0],s[k].ch[1]);
s[s[k].ch[0]].lz^=s[k].lz;s[s[k].ch[1]].lz^=s[k].lz;
s[k].lz=0;
}
}
void insert(int &k,int x,int d){
if(!k) k=++ncnt;if(!~d) return s[k].siz=1,void();
pushdown(k,d);insert(s[k].ch[x>>d&1],x,d-1);pushup(k);
}
int query(int k,int d){
if(!~d) return 0;pushdown(k,d);//printf("%d %d %d\n",k,d,s[k].siz);
if(s[s[k].ch[0]].siz>=(1<<d)) return query(s[k].ch[1],d-1)|(1<<d);
else return query(s[k].ch[0],d-1);
}
int merge(int x,int y,int d){
if(!x||!y) return x+y;int z=++ncnt;
if(!~d) return s[z].siz=1,z;
pushdown(x,d);pushdown(y,d);
s[z].ch[0]=merge(s[x].ch[0],s[y].ch[0],d-1);
s[z].ch[1]=merge(s[x].ch[1],s[y].ch[1],d-1);
return pushup(z),z;
}
void dfs(int x,int f){
int sum=0;vis[x]=1;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dfs(y,x);sum^=sg[y];
}
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
s[rt[y]].lz^=sg[y]^sum;rt[x]=merge(rt[x],rt[y],MAXB);
} insert(rt[x],sum,MAXB);sg[x]=query(rt[x],MAXB);
// printf("%d %d\n",x,sg[x]);
}
void solve(){
scanf("%d%d",&n,&m);clear();int sum=0;
for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0),sum^=sg[i];
printf("%s\n",(sum==0)?"Bob":"Alice");
}
int main(){int qu;scanf("%d",&qu);while(qu--) solve();return 0;}

UOJ 266 - 【清华集训2016】Alice和Bob又在玩游戏(SG 定理+01-trie)的更多相关文章

  1. uoj266[清华集训2016]Alice和Bob又在玩游戏(SG函数)

    uoj266[清华集训2016]Alice和Bob又在玩游戏(SG函数) uoj 题解时间 考虑如何求出每棵树(子树)的 $ SG $ . 众所周知一个状态的 $ SG $ 是其后继的 $ mex $ ...

  2. UOJ #266 【清华集训2016】 Alice和Bob又在玩游戏

    题目链接:Alice和Bob又在玩游戏 这道题就是一个很显然的公平游戏. 首先\(O(n^2)\)的算法非常好写.暴力枚举每个后继计算\(mex\)即可.注意计算后继的时候可以直接从父亲转移过来,没必 ...

  3. [UOJ#274][清华集训2016]温暖会指引我们前行

    [UOJ#274][清华集训2016]温暖会指引我们前行 试题描述 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了一 ...

  4. [UOJ266]Alice和Bob又在玩游戏

    [UOJ266]Alice和Bob又在玩游戏 Tags:题解 作业部落 评论地址 TAG:博弈 题意 不同于树的删边游戏,删掉一个点删去的是到根的路径 题解 这题只和计算\(SG\)有关,博弈的有关内 ...

  5. UOJ#266. 【清华集训2016】Alice和Bob又在玩游戏 博弈,DSU on Tree,Trie

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ266.html 题解 首先我们可以直接暴力 $O(n^2)$ 用 sg 函数来算答案. 对于一个树就是枚举 ...

  6. [BZOJ4730][清华集训2016][UOJ266] Alice和Bob又在玩游戏

    题意:俩智障又在玩游戏.规则如下: 给定n个点,m条无向边(m<=n-1),保证无环,对于每一个联通块,编号最小的为它们的根(也就是形成了一片这样的森林),每次可以选择一个点,将其本身与其祖先全 ...

  7. bzoj4730: Alice和Bob又在玩游戏

    Description Alice和Bob在玩游戏.有n个节点,m条边(0<=m<=n-1),构成若干棵有根树,每棵树的根节点是该连通块内编号最 小的点.Alice和Bob轮流操作,每回合 ...

  8. uoj#266. 【清华集训2016】Alice和Bob又在玩游戏(博弈论)

    传送门 完了我连sg函数是个啥都快忘了 设\(sg[u]\)为以\(u\)为根节点的子树的\(sg\)函数值,\(rem[u]\)表示\(u\)到根节点的路径删掉之后剩下的游戏的异或值 根节点\(u\ ...

  9. 【清华集训2016】Alice和Bob又在玩游戏

    不难的题目.因为SG性质,所以只需要对一棵树求出. 然后如果发现从上往下DP不太行,所以从下往上DP. 考虑一个点对子树的合并,考虑下一个删的点在哪一个子树,那么剩下的状态实际上就是把一个子树所有能达 ...

随机推荐

  1. 阿里大神favoorr提供的书单

     Thoughtwoks中国的推荐书单 <http://www.douban.com/doulist/2012097/ >新浪微博-秦迪 <http://blog.2baxb.me/ ...

  2. 2020BUAA软工个人博客作业

    2020BUAA软工个人博客作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业 我在这个课程的目标是 学 ...

  3. BUAA2020软工作业——提问回顾与个人总结

    项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 提问回顾与个人总结 我在这个课程的目标是 进一步提高自己的编码能力,工程能力 这个作业在哪个具体方 ...

  4. eureka服务端和客户端的简单搭建

    本篇博客简单记录一下,eureka 服务端和 客户端的简单搭建. 目标: 1.完成单机 eureka server 和 eureka client 的搭建. 2.完成eureka server 的添加 ...

  5. Redis核心原理与实践--Redis启动过程源码分析

    Redis服务器负责接收处理用户请求,为用户提供服务. Redis服务器的启动命令格式如下: redis-server [ configfile ] [ options ] configfile参数指 ...

  6. Qt学习-模仿Qt实现一个colorbutton

    1.mycolorbutton.h #include<QToolButton> #include<QtGlobal> #include<QColor> class ...

  7. Linux内核漏洞精准检测如何做?SCA工具不能只在软件层面

    摘要:二进制SCA工具要想更好的辅助安全人员实现安全审计.降低漏洞检测的误报率,必须向更细颗粒度的检测维度发展,而不仅仅停留在开源软件的层面,同时对漏洞库的要求也需要向细颗粒度的精准信息提出的挑战. ...

  8. linux shell 函数返回值问题(超过255)

    最近再写一个shell测试的时候出现问题,函数返回值异常 用shell计算斐波那契数列数列,写了一个shell函数,然后调用的,验证的时候我只随便计算了几个数(10以内),确认结果是正确的就提交了,后 ...

  9. Beam Search快速理解及代码解析

    目录 Beam Search快速理解及代码解析(上) Beam Search 贪心搜索 Beam Search Beam Search代码解析 准备初始输入 序列扩展 准备输出 总结 Beam Sea ...

  10. 修改记事本PE结构弹计算器Shellcode

    目录 修改记事本PE结构弹计算器Shellcode 0x00 前言 0x01 添加新节 修改节数量 节表位置 添加新节表信息 0x02 添加弹计算器Shellcode 修改代码 0x03 修改入口点 ...