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 node is either black or white initially.
They take turns to do the following operation:
Choose a white node v from the current tree;
Color all white nodes on Path(1,v) to black.
The player who takes the last turn wins.
Now Alice takes the first turn.Help her find out if she can win when they both use optimal strategy.
Input
The first line of input contains a integer n representing the number of nodes in the tree. 1<=n<=100000
The second line contains n intergers c1,c2,..cn.0<=ci<=1.
ci=0 means the ith node is white initially and ci=1 means black.
Next n-1 lines describes n-1 edges in the tree.Each line contains two integers u and v,means there is a edge connecting u and v.
Output
If Alice can't win print -1.
Otherwise determine all the nodes she can choose in the first turn in order to win.Print them in ascending order.
题目大意:(以下题意摘自《线段树的合并》——黄嘉泰,标点符号有改动……)
给定一棵n个点的有根树,每个点是黑的或者白的。 两个人在树上博弈,轮流进行以下操作: 选择一个当前为白色的点u,把u到根路径上的所有点涂黑。不能操作者输,判断两人都用最优策略进行游戏时的胜负情况,并输出第一个人第一步所有可行的决策。
思路:(以下思路摘自《线段树的合并》——黄嘉泰)
由公平组合游戏的性质不难想到以下的dp:
---下面的所有+运算都表示xor
dp[u]表示只考虑子树u的SG值
g[u][v]表示只考虑子树u,v是u的某个白色后代(可能为u),第一步选择了v,将u到v全部涂黑后的局面的SG值
dp[u]=mex(g[u]),关键是求g[u]
---当u是白色时,新增g[u][u]=sigma{dp[v]|v是u的儿子}
---g[u][w]=g[v][w]+sigma{dp[v]|v是u的儿子且v!=branch[w]} // 注释:branch[w]=v即,v是u的儿子且v是w的祖先
---g[u][w]=g[v][w]+g[u][u]+dp[branch[w]] // 注释:这一行是上一行的解,其中g[u][u]=sigma{dp[v]|v是u的儿子}
$O(n^2)$
我们可以做的更好
注意到转移过程中,来自同一子树的g值都被xor上了同一个数,最后所有g值被放在一起进行mex
如果选用某种数据结构,能够快速地完成整体xor,再合并的操作,并且高效地支持mex运算,就可以改进复杂度。
二进制Trie
启发式合并,对最大子树的Trie打标记,其余dp值暴力插入
mex(T)=size(T->l)==cnt(T->l)?size(T->l)+mex(T->r):mex(T->l)
复杂度$O(n log^2n)$
瓶颈是合并操作
二进制Trie和线段树非常类似,使用之前的过程高效合并
传递标记/询问mex/合并树 都是自顶向下的,不矛盾
$O(n logn)$
——————————————————————————摘录完毕的分割线——————————————————————————————————
关于合并操作可以去看《线段树的合并》——黄嘉泰。
关于输出解,随便DFS一下即可,详细可以见dfs_ans函数。
PS:这东东就没有什么高大上的名字吗?
PS2:玛雅AC之后发现忘了输出-1,唉不管了。
代码(C++14 0.55S):
#include <bits/stdc++.h>
using namespace std;
#define FOR(i, n) for(int i = 0; i < n; ++i) const int MAXV = ;
const int MAXE = MAXV << ; const int white = ;
int max_log; int head[MAXV], color[MAXV], ecnt;
int to[MAXE], nxt[MAXE];
int n; void initGraph() {
memset(head + , -, n * sizeof(int));
ecnt = ;
} void add_edge(int u, int v) {
to[ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt++;
to[ecnt] = u; nxt[ecnt] = head[v]; head[v] = ecnt++;
} struct Node {
Node* go[];
int size, txor, mex;
};
Node statePool[ * MAXV];
Node *nil, *leaf;
Node* stk[ * MAXV];
int ncnt, top; Node *new_node() {
Node* t = top ? stk[--top] : &statePool[ncnt++];
FOR(i, ) t->go[i] = nil;
t->size = t->txor = t->mex = ;
return t;
} void remove(Node *t) {
stk[top++] = t;
} void initTree() {
nil = statePool;
FOR(i, ) nil->go[i] = nil;
ncnt = ;
leaf = new_node();
leaf->mex = leaf->size = ;
} void pushdown(Node *t, int k) {
if(k > ) {
if((t->txor >> (k - )) & ) swap(t->go[], t->go[]);
FOR(i, ) t->go[i]->txor ^= t->txor;
t->txor = ;
}
} void update(Node *t, int k) {
if(k > ) {
int size = << (k - );
t->mex = (t->go[]->size < size ? t->go[]->mex : size + t->go[]->mex);
t->size = t->go[]->size + t->go[]->size;
}
} Node* merge(Node *a, Node *b, int k) {
if(a == nil) return b;
if(b == nil) return a;
if(a == leaf && b == leaf) return leaf; Node *res = new_node();
pushdown(a, k), pushdown(b, k);
FOR(i, ) res->go[i] = merge(a->go[i], b->go[i], k - );
update(res, k);
remove(a), remove(b);
return res;
} void insert(Node* &t, int k, int val) {
if(k == ) t = leaf;
else {
if(t == nil) t = new_node();
pushdown(t, k);
insert(t->go[(val >> (k - )) & ], k - , val);
update(t, k);
}
} Node *root[MAXV];
int dp[MAXV]; void dfs(int u, int f) {
int tmp = ;
for(int p = head[u]; ~p; p = nxt[p]) {
int v = to[p];
if(v != f) dfs(v, u), tmp ^= dp[v];
}
if(color[u] == white) insert(root[u], max_log, tmp);
for(int p = head[u]; ~p; p = nxt[p]) {
int v = to[p];
if(v == f) continue;
root[v]->txor ^= tmp ^ dp[v];
root[u] = merge(root[u], root[v], max_log);
}
dp[u] = root[u]->mex;
} vector<int> ans; void dfs_ans(int u, int f, int sg) {
int tmp = ;
for(int p = head[u]; ~p; p = nxt[p]) {
int v = to[p];
if(v != f) tmp ^= dp[v];
}
if(color[u] == white && (sg ^ tmp) == ) ans.push_back(u);
for(int p = head[u]; ~p; p = nxt[p]) {
int v = to[p];
if(v != f) dfs_ans(v, u, sg ^ tmp ^ dp[v]);
}
} int main() {
scanf("%d", &n);
for(int i = ; i <= n; ++i) scanf("%d", &color[i]);
initGraph();
for(int i = , u, v; i < n; ++i) {
scanf("%d%d", &u, &v);
add_edge(u, v);
}
initTree();
while(( << max_log) <= n) ++max_log; for(int i = ; i <= n; ++i) root[i] = nil;
dfs(, );
dfs_ans(, , ); sort(ans.begin(), ans.end());
for(int x : ans) printf("%d\n", x);
}
SPOJ COT3 Combat on a tree(Trie树、线段树的合并)的更多相关文章
- SPOJ COT3.Combat on a tree(博弈论 Trie合并)
题目链接 \(Description\) 给定一棵\(n\)个点的树,每个点是黑色或白色.两个人轮流操作,每次可以选一个白色的点,将它到根节点路径上的所有点染黑.不能操作的人输,求先手是否能赢.如果能 ...
- SPOJ COT3 - Combat on a tree
/* 考虑直接使用暴力来算的话 SG[i]表示以i为根的子树的SG值, 然后考虑枚举删除那个子树节点, 然后求拆成的树的sg异或值, 求mex即可 复杂度三次方 然后考虑尝试 整体来做 发现对于每次子 ...
- BZOJ - 2588 Spoj 10628. Count on a tree (可持久化线段树+LCA/树链剖分)
题目链接 第一种方法,dfs序上建可持久化线段树,然后询问的时候把两点之间的所有树链扒出来做差. #include<bits/stdc++.h> using namespace std; ...
- SPOJ 10628. SPOJ COT Count on a tree 可持久化线段树
这题是裸的主席树,每个节点建一棵主席树,再加个lca就可以了. 历尽艰辛,终于A掉了这一题,这般艰辛也显示出了打代码的不熟练. 错误:1.lca倍增的时候i和j写反了,RE了5次,实在要吸取教训 2. ...
- POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和)
POJ.3321 Apple Tree ( DFS序 线段树 单点更新 区间求和) 题意分析 卡卡屋前有一株苹果树,每年秋天,树上长了许多苹果.卡卡很喜欢苹果.树上有N个节点,卡卡给他们编号1到N,根 ...
- 2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)
2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点 ...
- 浅谈可持久化Trie与线段树的原理以及实现(带图)
浅谈可持久化Trie与线段树的原理以及实现 引言 当我们需要保存一个数据结构不同时间的每个版本,最朴素的方法就是每个时间都创建一个独立的数据结构,单独储存. 但是这种方法不仅每次复制新的数据结构需要时 ...
- 浅谈树套树(线段树套平衡树)&学习笔记
0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...
- 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 ...
随机推荐
- Java高级之虚拟机垃圾回收机制
博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved ! 区别于C语言手动回收,Java自动执行垃圾回收,但为了执行高效 ...
- Python基本数据类型之list列表
列表是python中用的非常频繁的数据结构,它是有序序列.之前学的字符串就是一种有序序列.不过列表是可变的. 创建列表 li = list( ) #构 ...
- 关于c语言二维数组与指针的个人理解及处理办法。
相信大家在学习C语言时,对一维数组和指针的理解应该是自信的,但是,我在学习过程中,看到网上一些博文,发现即便是参加工作的一些专业编程人员,突然碰到二维数组和指针的问题时,也可能会遇到难以处理的诡异问题 ...
- eclipse根据.wsdl文件自动生成webservice的调用客户端
1.工具:eclipse3.3或者是带有webservice插件的eclipse 2. 首先用浏览器访问webservice的站点,接着保存打开的页面,后缀为.wsdl. 3.把保存好的文件拷入ecl ...
- Javascript实现提示错误的信息直接出现在输入框后
可以在输入框后加个<span id="error"></span>,当验证错误时这样处理document.getElementById('error').i ...
- JQuery直接调用asp.net后台WebMethod方法
利用JQuery的$.ajax()可以很方便的调用asp.net的后台方法.[WebMethod] 命名空间 1.无参数的方法调用, 注意:1.方法一定要静态方法,而且要有[WebMethod]的 ...
- notepad++代码折叠对应的树形结构快捷键
树形层次,从1开始计数 <!doctype html> <html lang="en" class="1"> <head clas ...
- focus 、blur和focusin,focusout的区别
$('input').focus(function(){///当前元素触发事件(特指表单) }); $('input').blur(function(){//当前元素触发事件 }); $('div') ...
- Android RecycleView + CardView 控件简析
今天使用了V7包加入的RecycleView 和 CardView,写篇简析. 先上效果图: 原理图: 这是RecycleView的工作原理: 1.LayoutManager用来处理RecycleVi ...
- 30天,O2O速成攻略【8.16武汉站】
活动概况 时间:2015年08月16日13:30-16:30 地点:光谷创业咖啡(光谷广场资本大厦一楼停车场内) 主办:APICloud.爱立示.MBA移动业务助理 网址:www.apicloud.c ...