SPOJ COT3.Combat on a tree(博弈论 Trie合并)
\(Description\)
给定一棵\(n\)个点的树,每个点是黑色或白色。两个人轮流操作,每次可以选一个白色的点,将它到根节点路径上的所有点染黑。不能操作的人输,求先手是否能赢。如果能,输出第一步选择哪些节点能赢。
\(n\leq10^5\)。
\(Solution\)
对于叶子节点,如果能染色,\(SG(x)=1\),否则\(=0\)。
考虑从下往上算每棵子树的\(SG\)值。设\(SG(x)\)表示\(x\)子树的\(SG\)值,\(g(x)\)表示对\(x\)这棵子树操作能得到的后继的\(SG\)值集合(只考虑\(x\)子树),那么\(SG(x)=\mathbb{mex}\{g(x)\}\)。
考虑如何计算\(g(x)\)。令\(sum[x]=sg(v_1)\ \mathbb{xor}\ sg(v_2)\ \mathbb{xor}...,\ v_i\in son[x]\)。
若\(x\)是黑点,假设这次操作选了\(v_i\)子树中的某个点,那么其它子树状态不变,\(v_i\)子树的后继状态会变成\(g(v_i)\)中的某个,所以\(g(x)=sum[x]\ \mathbb{xor}\ sg(v_i)\ \mathbb{xor}\ (g(v_i)中的某个值)\)。
把子树内每个\(g(v_i)\)整体\(\mathbb{xor}\)一个数,合并起来,就可以得到\(g(x)\)了。
若\(x\)是白点,多了一种选\(x\)的后继,选\(x\)后得到状态的\(SG\)值就是\(sum[x]\)。所以在\(g(x)\)中再插入一个\(sum[x]\)即可。
还要支持求\(\mathbb{mex}\),可以用\(01Trie\)维护。合并的时候可以启发式合并,\(O(n\log^2n)\),也可以类似线段树合并做到\(O(n\log n)\)。
对于第二问,考虑选择一个节点后局面的\(SG\)值。容易发现就是除去它到根节点路径上的点的所有点的\(SG\)值的异或和。
记\(up[x]\)表示除去\(x\)到根节点路径上的点外,所有节点的\(SG\)值异或和,那么\(up[x]=up[fa[x]]^{\wedge}sum[fa[x]]^{\wedge}SG(x)\)。选择\(x\)后\(x\)的各棵子树是独立的,局面的\(SG\)值就是\(up[x]^{\wedge}sum[x]\)(\(up\)也可以直接\(DFS\)的时候传个参)。
所以如果\(up[x]^{\wedge}sum[x]=0\),选这个点就是必胜的。
这个\(SG\)值的最大值是啥啊...
注意\(Trie\)要特判叶子的地方(尤其是Merge,注意像线段树合并一样判下叶子)(我怎么老是写错...)。
//0.11s 35M
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define Bit 16
#define gc() getchar()
#define MAXIN 500000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;
int Enum,H[N],nxt[N<<1],to[N<<1],root[N],sg[N],sum[N],up[N];
bool col[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Trie
{
#define S N*20
#define ls son[x][0]
#define rs son[x][1]
int tot,son[S][2],tag[S];
bool full[S];
#define Update(x) full[x]=full[ls]&&full[rs]
inline void Upd(int x,int v,int dep)
{
// if(dep<0) return;
if(v>>dep&1) std::swap(ls,rs);
tag[x]^=v;
}
inline void PushDown(int x,int dep)
{
if(dep&&tag[x]) Upd(ls,tag[x],dep-1), Upd(rs,tag[x],dep-1), tag[x]=0;
}
void Insert(int &x,int v,int dep)
{
x=++tot;
if(dep<0) {full[x]=1; return;}
v>>dep&1 ? Insert(rs,v,dep-1) : Insert(ls,v,dep-1);
}
int Merge(int x,int y,int dep)
{
if(!x||!y) return x|y;
if(dep<0) return x;
PushDown(x,dep), PushDown(y,dep);
ls=Merge(ls,son[y][0],dep-1), rs=Merge(rs,son[y][1],dep-1);
Update(x); return x;
}
int Mex(int x,int dep)
{
if(!x||dep<0) return 0;
PushDown(x,dep);
return full[ls]?(1<<dep)+Mex(rs,dep-1):Mex(ls,dep-1);
}
}T;
inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-48,c=gc());
return now;
}
inline void AE(int u,int v)
{
to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS1(int x,int fa)
{
if(!col[x]) T.Insert(root[x],0,Bit);
int s=0;
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa)
DFS1(v,x), s^=sg[v], T.Upd(root[v],sg[v],Bit), root[x]=T.Merge(root[x],root[v],Bit);
if(s) T.Upd(root[x],s,Bit);
sum[x]=s, sg[x]=T.Mex(root[x],Bit);
}
void DFS2(int x,int fa)
{
up[x]=up[fa]^sum[fa]^sg[x];
for(int i=H[x],v; i; i=nxt[i])
if((v=to[i])!=fa) DFS2(v,x);
}
int main()
{
const int n=read();
for(int i=1; i<=n; ++i) col[i]=read();
for(int i=1; i<n; ++i) AE(read(),read());
DFS1(1,0), sum[0]=sg[1], DFS2(1,0);
bool f=0;
for(int x=1; x<=n; ++x) if(!col[x]&&!(sum[x]^up[x])) f=1, printf("%d\n",x);
if(!f) puts("-1");
return 0;
}
SPOJ COT3.Combat on a tree(博弈论 Trie合并)的更多相关文章
- SPOJ COT3 Combat on a tree(Trie树、线段树的合并)
题目链接:http://www.spoj.com/problems/COT3/ Alice and Bob are playing a game on a tree of n nodes.Each n ...
- SPOJ COT3 - Combat on a tree
/* 考虑直接使用暴力来算的话 SG[i]表示以i为根的子树的SG值, 然后考虑枚举删除那个子树节点, 然后求拆成的树的sg异或值, 求mex即可 复杂度三次方 然后考虑尝试 整体来做 发现对于每次子 ...
- BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]
2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MBSubmit: 5217 Solved: 1233 ...
- BZOJ 2588: Spoj 10628. Count on a tree 树上跑主席树
2588: Spoj 10628. Count on a tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/J ...
- Bzoj 2588: Spoj 10628. Count on a tree 主席树,离散化,可持久,倍增LCA
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2588 2588: Spoj 10628. Count on a tree Time Limit ...
- BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )
Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...
- 【BZOJ2589】 Spoj 10707 Count on a tree II
BZOJ2589 Spoj 10707 Count on a tree II Solution 吐槽:这道题目简直...丧心病狂 如果没有强制在线不就是树上莫队入门题? 如果加了强制在线怎么做? 考虑 ...
- 【BZOJ2588】Spoj 10628. Count on a tree 主席树+LCA
[BZOJ2588]Spoj 10628. Count on a tree Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lasta ...
- 【SPOJ】QTREE7(Link-Cut Tree)
[SPOJ]QTREE7(Link-Cut Tree) 题面 洛谷 Vjudge 题解 和QTREE6的本质是一样的:维护同色联通块 那么,QTREE6同理,对于两种颜色分别维护一棵\(LCT\) 每 ...
随机推荐
- poj2513--并查集+欧拉路+字典树
经典好题,自己不知道哪里错了交上去是RE,可能是数组开的不好吧,字典树老碰到这种问题.. 先马上别人的代码,有空对拍看看 #include <cstdio> #include <cs ...
- python之路第二天
为何要有操作系统 为了让程序员更轻松的完成命令电脑工作而存在的,控制硬件,服务于软件. 操作系统的位置 操作系统位于软件和硬件之间.操作系统由内核(运行于内核态,控制硬件)和系统调用(运行于用户态,为 ...
- CF 1042F
玄学贪心... 题意:给出一棵树,要求将他的所有叶节点分成最少的组,且在每组中的任意两节点之间的距离不大于k 解析: 显然是个贪心啦... 稍微考虑一下贪心思想: 我们从下向上合并整棵树,在合并到某个 ...
- 动手动脑——JAVA语法基础
EnumTest.java public class EnumTest { public static void main(String[] args) { Size s=Size.SMALL; Si ...
- OpenCV-Python入门教程7-PyQt编写GUI界面
前面一直都是使用命令行运行代码,不够人性化.这篇用Python编写一个GUI界面,使用PyQt5编写图像处理程序.包括:打开.关闭摄像头,捕获图片,读取本地图片,灰度化和Otsu自动阈值分割的功能. ...
- 实战--使用lvs实现四层负载均衡,转发到后端nginx
这个帖子讲得很细致,基本依照这个方案实践. 只是IP是按我自己虚拟机的IP来测试的. http://www.cnblogs.com/arjenlee/p/9262737.html ========== ...
- 认识IQueryable和IQueryProvider接口
1.Func<Student, bool>和Expression<Func<Student, bool>>的区别 class Program { static vo ...
- Lambda表达式树解析(下)
概述 前面章节,总结了Lambda树的构建,那么怎么解析Lambda表达式树那?Lambda表达式是一种委托构造而成,如果能够清晰的解析Lambda表达式树,那么就能够理解Lambda表达式要传递的正 ...
- Spring MVC基础知识整理➣环境搭建和Hello World
概述 Spring MVC属于SpringFrameWork的产品,采用Model-View-Controller进行数据交互,已经融合在Spring Web Flow里面.Spring 框架提供了构 ...
- python全栈开发day68-ORM操作:一般操作、ForeignKey操作、ManyToManyField、聚合查询和分组查询、F查询和Q查询等
ORM操作 https://www.cnblogs.com/maple-shaw/articles/9403501.html 一.一般操作 1. 必知必会13条 <1> all(): 查询 ...