zoukankan      html  css  js  c++  java
  • 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):

      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
    ", x);
    143 }
    View Code
  • 相关阅读:
    解决 idea 项目中Error:java: 无效的标记: XX:MaxPermSize=512M
    vant预览图片
    react路由
    computed和watch
    仓库系统面单常用的打印插件
    04.简单了解一下Redis企业级数据备份方案
    CRMEB 源码 login页 获取信息 缓存修改
    frp 搭建远程桌面
    ABP asp.net core 项目发布 IIS部署
    MYSQL 监控数据库SQL语句 查看数据库执行语句
  • 原文地址:https://www.cnblogs.com/oyking/p/4266505.html
Copyright © 2011-2022 走看看