码迷,mamicode.com
首页 > 其他好文 > 详细

SPOJ COT3 Combat on a tree(Trie树、线段树的合并)

时间:2015-02-01 23:19:06      阅读:986      评论:0      收藏:0      [点我收藏+]

标签:

题目链接: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]}
---g[u][w]=g[v][w]+g[u][u]+dp[branch[w]]

 

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(nlog^2n)

 

瓶颈是合并操作
二进制Trie和线段树非常类似,使用之前的过程高效合并
传递标记/询问mex/合并树 都是自顶向下的,不矛盾
O(nlogn)

——————————————————————————摘录完毕的分割线——————————————————————————————————

关于合并操作可以去看《线段树的合并》——黄嘉泰。

关于输出解,随便DFS一下即可,详细可以见dfs_ans函数。

PS:这东东就没有什么高大上的名字吗?

PS2:玛雅AC之后发现忘了输出-1,唉不管了。

 

代码(C++14 0.55S):

技术分享
  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 #define FOR(i, n) for(int i = 0; i < n; ++i)
  4 
  5 const int MAXV = 100010;
  6 const int MAXE = MAXV << 1;
  7 
  8 const int white = 0;
  9 int max_log;
 10 
 11 int head[MAXV], color[MAXV], ecnt;
 12 int to[MAXE], nxt[MAXE];
 13 int n;
 14 
 15 void initGraph() {
 16     memset(head + 1, -1, n * sizeof(int));
 17     ecnt = 0;
 18 }
 19 
 20 void add_edge(int u, int v) {
 21     to[ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt++;
 22     to[ecnt] = u; nxt[ecnt] = head[v]; head[v] = ecnt++;
 23 }
 24 
 25 struct Node {
 26     Node* go[2];
 27     int size, txor, mex;
 28 };
 29 Node statePool[20 * MAXV];
 30 Node *nil, *leaf;
 31 Node* stk[20 * MAXV];
 32 int ncnt, top;
 33 
 34 Node *new_node() {
 35     Node* t = top ? stk[--top] : &statePool[ncnt++];
 36     FOR(i, 2) t->go[i] = nil;
 37     t->size = t->txor = t->mex = 0;
 38     return t;
 39 }
 40 
 41 void remove(Node *t) {
 42     stk[top++] = t;
 43 }
 44 
 45 void initTree() {
 46     nil = statePool;
 47     FOR(i, 2) nil->go[i] = nil;
 48     ncnt = 1;
 49     leaf = new_node();
 50     leaf->mex = leaf->size = 1;
 51 }
 52 
 53 void pushdown(Node *t, int k) {
 54     if(k > 0) {
 55         if((t->txor >> (k - 1)) & 1) swap(t->go[0], t->go[1]);
 56         FOR(i, 2) t->go[i]->txor ^= t->txor;
 57         t->txor = 0;
 58     }
 59 }
 60 
 61 void update(Node *t, int k) {
 62     if(k > 0) {
 63         int size = 1 << (k - 1);
 64         t->mex = (t->go[0]->size < size ? t->go[0]->mex : size + t->go[1]->mex);
 65         t->size = t->go[0]->size + t->go[1]->size;
 66     }
 67 }
 68 
 69 Node* merge(Node *a, Node *b, int k) {
 70     if(a == nil) return b;
 71     if(b == nil) return a;
 72     if(a == leaf && b == leaf) return leaf;
 73 
 74     Node *res = new_node();
 75     pushdown(a, k), pushdown(b, k);
 76     FOR(i, 2) res->go[i] = merge(a->go[i], b->go[i], k - 1);
 77     update(res, k);
 78     remove(a), remove(b);
 79     return res;
 80 }
 81 
 82 void insert(Node* &t, int k, int val) {
 83     if(k == 0) t = leaf;
 84     else {
 85         if(t == nil) t = new_node();
 86         pushdown(t, k);
 87         insert(t->go[(val >> (k - 1)) & 1], k - 1, val);
 88         update(t, k);
 89     }
 90 }
 91 
 92 Node *root[MAXV];
 93 int dp[MAXV];
 94 
 95 void dfs(int u, int f) {
 96     int tmp = 0;
 97     for(int p = head[u]; ~p; p = nxt[p]) {
 98         int v = to[p];
 99         if(v != f) dfs(v, u), tmp ^= dp[v];
100     }
101     if(color[u] == white) insert(root[u], max_log, tmp);
102     for(int p = head[u]; ~p; p = nxt[p]) {
103         int v = to[p];
104         if(v == f) continue;
105         root[v]->txor ^= tmp ^ dp[v];
106         root[u] = merge(root[u], root[v], max_log);
107     }
108     dp[u] = root[u]->mex;
109 }
110 
111 vector<int> ans;
112 
113 void dfs_ans(int u, int f, int sg) {
114     int tmp = 0;
115     for(int p = head[u]; ~p; p = nxt[p]) {
116         int v = to[p];
117         if(v != f) tmp ^= dp[v];
118     }
119     if(color[u] == white && (sg ^ tmp) == 0) ans.push_back(u);
120     for(int p = head[u]; ~p; p = nxt[p]) {
121         int v = to[p];
122         if(v != f) dfs_ans(v, u, sg ^ tmp ^ dp[v]);
123     }
124 }
125 
126 int main() {
127     scanf("%d", &n);
128     for(int i = 1; i <= n; ++i) scanf("%d", &color[i]);
129     initGraph();
130     for(int i = 1, u, v; i < n; ++i) {
131         scanf("%d%d", &u, &v);
132         add_edge(u, v);
133     }
134     initTree();
135     while((1 << max_log) <= n) ++max_log;
136 
137     for(int i = 1; i <= n; ++i) root[i] = nil;
138     dfs(1, 0);
139     dfs_ans(1, 0, 0);
140 
141     sort(ans.begin(), ans.end());
142     for(int x : ans) printf("%d\n", x);
143 }
View Code

 

SPOJ COT3 Combat on a tree(Trie树、线段树的合并)

标签:

原文地址:http://www.cnblogs.com/oyking/p/4266505.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!