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 ...
随机推荐
- php项目域名绑定和替换
---恢复内容开始--- 在apache的httpd.conf里面找到 DocumentRoot "E:/xampp/htdocs"<Directory "E:/x ...
- freebsd上安装nginx+php记录
参考文章 https://wiki.freebsdchina.org/faq/ports http://www.vpsee.com/2014/04/install-nginx-php-apc-mysq ...
- Java高级之内存模型分析
博客出自:http://blog.csdn.net/liuxian13183,转载注明出处! All Rights Reserved ! 下文是博主感悟,请带着怀疑性的态度阅读! 需要了解基本变量所占 ...
- javax.management.NotCompliantMBeanException
public interface QueueMBean { } 假如接口名叫 XMBean ,那么实现名就必须一定是X,而且是大小写敏感的. public class Queue implements ...
- Linux下编译静态MinGW环境,编译windows平台Qt程序(使用MXE)
参考链接: MXE.>大多数程序都是在windows平台下开发的程序.windows 在现实中也是绕不过的一个系统平台,做为受过几年VC,MFC”虐待”的程序员,在做为一个程序员之前是一位Lin ...
- background:transparent的作用
background的属性值 background : background-color | background-image | background-repeat | background-att ...
- 各个JSON技术的比较
JSON技术的调研报告 一 .各个JSON技术的简介和优劣1.json-libjson-lib最开始的也是应用最广泛的json解析工具,json-lib 不好的地方确实是依赖于很多第三方包,包括com ...
- VLC说明
一.简介 vlc的全名是Video Lan Client,是一个开源的.跨平台的视频播放器.VLC支持大量的音视频传输.封装和编码格式,完整的功能特性列表可以在这里获得http://www.video ...
- 通过SessionID和用户名来保证同一个用户不能同时登录(单点登录)
可以通过SessionID和用户名来保证同一个用户不能同时登录的问题,下面程序模仿了QQ的登录,当登录后判断当前帐号是否已经登录,如果登录.则踢掉以前登录的用户. 1.通过Application全局变 ...
- LeetCode Expression Add Operators
原题链接在这里:https://leetcode.com/problems/expression-add-operators/ 题目: Given a string that contains onl ...