SPOJ11414 COT3 博弈论 + Trie树合并



考虑对于每个子树从下往上依次考虑

对于叶子节点而言,如果可以染色,那么其\(sg\)值为\(1\),否则为\(0\)

考虑往上合并

如果选择了\(x\),那么后继状态就是其所有子树

如果选了其他子树中的一点,那么后继状态的构成如图所示

也就是,到当前根为止的所有其他子树的\(sg\)值异或上本身

那么,我们可以考虑维护一个数据结构,每次往上的时候,对于一棵子树内的点,异或上其他子树的\(sg\)值

至于查\(sg\)值,可以用一个支持查\(mex\)的东西

还需要合并

\(Trie\)树是一个不错的选择

输出答案就随意\(dfs\)一下,思路和上面的差不多

复杂度\(O(n \log n)\)


#include <map>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define ri register int
#define rep(io, st, ed) for(ri io = st; io <= ed; io ++)
#define drep(io, ed, st) for(ri io = ed; io >= st; io --)

const int sid = 2e5 + 5;
const int cid = 2e7 + 5;

#define gc getchar
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') p = p * 10 + c - '0', c = gc();
    return p * w;
}

bool cov[cid];
int n, id, tot, cnp;
int q[sid], sg[sid], ls[cid], rs[cid], xr[cid];
int rt[sid], col[sid], cap[sid], nxt[sid], node[sid];

inline void addedge(int u, int v) {
    nxt[++ cnp] = cap[u]; cap[u] = cnp; node[cnp] = v;
}

inline void put_xor(int &o, int val, int dep) {
    if(dep <= -1) return;
    if(val & (1 << dep)) swap(ls[o], rs[o]);
    xr[o] ^= val;
}

inline void pushdown(int o, int dep) {
    if(!xr[o] || !o) return;
    put_xor(ls[o], xr[o], dep - 1);
    put_xor(rs[o], xr[o], dep - 1);
    xr[o] = 0;
}

inline void insert(int &o, int val, int dep) {
    if(!o) o = ++ id;
    if(dep == -1) { cov[o] = 1; return; }
    if(val & (1 << dep)) insert(rs[o], val, dep - 1);
    else insert(ls[o], val, dep - 1);
}

inline int merge(int x, int y, int dep) {
    if(!x || !y) return x + y;
    if(dep == -1) { cov[x] |= cov[y]; return x; }
    pushdown(x, dep); pushdown(y, dep);
    ls[x] = merge(ls[x], ls[y], dep - 1);
    rs[x] = merge(rs[x], rs[y], dep - 1);
    cov[x] = cov[ls[x]] && cov[rs[x]];
    return x;
}

inline int mex(int o, int dep) {
    if(!o || dep == -1) return 0;
    pushdown(o, dep);
    if(!cov[ls[o]]) return mex(ls[o], dep - 1);
    else return (1 << dep) + mex(rs[o], dep - 1);
}

#define cur node[i]
inline void dfs(int o, int fa) {
    int nsg = 0;
    for(int i = cap[o]; i; i = nxt[i])
        if(cur != fa) dfs(cur, o), nsg ^= sg[cur];
    if(!col[o]) insert(rt[o], nsg, 17);
    for(int i = cap[o]; i; i = nxt[i])
        if(cur != fa) {
            put_xor(rt[cur], nsg ^ sg[cur], 17);
            rt[o] = merge(rt[o], rt[cur], 17);
        }
    sg[o] = mex(rt[o], 17);
}

inline void find(int o, int fa, int SG) {
    for(int i = cap[o]; i; i = nxt[i])
        if(cur != fa) SG ^= sg[cur];
    if(SG == 0 && !col[o]) q[++ tot] = o;
    for(int i = cap[o]; i; i = nxt[i])
    if(cur != fa) find(cur, o, SG ^ sg[cur]);
}

int main() {
    n = read();
    rep(i, 1, n) col[i] = read();
    rep(i, 2, n) {
        int u = read(), v = read();
        addedge(u, v); addedge(v, u);
    }
    dfs(1, 0);
    find(1, 0, 0);
    if(tot) {
        sort(q + 1, q + tot + 1);
        rep(i, 1, tot) printf("%d\n", q[i]);
    }
    else puts("-1");
    return 0;
}

原文地址:https://www.cnblogs.com/reverymoon/p/10197772.html

时间: 2024-07-30 23:56:32

SPOJ11414 COT3 博弈论 + 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 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 node

【CF778C】Peterson Polyglot(Trie树,启发式合并)

题意:有一棵n个结点的只由小写字母组成的Trie树,给定它的具体形态,问删除哪一层后剩下Trie树的结点数最少 n<=3e5 思路:先建出原Trie树,对于每一层的每一个结点计算删除后对答案的贡献,这一部分使用启发式合并 官方题解证明了时间复杂度是一个log的 http://codeforces.com/blog/entry/50724 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #i

CodeForces - 778C: Peterson Polyglot (启发式合并trie树)

Peterson loves to learn new languages, but his favorite hobby is making new ones. Language is a set of words, and word is a sequence of lowercase Latin letters. Peterson makes new language every morning. It is difficult task to store the whole langua

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

【转】B树、B-树、B+树、B*树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介

B  树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中:否则,如果查询关键字比结点关键字小,就进入左儿子:如果比结点关键字大,就进入右儿子:如果左儿子或右儿子的指针为空,则报告找不到相应的关键字: 如果B树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性

B树、Trie树详解

查找(二) 散列表 散列表是普通数组概念的推广.由于对普通数组可以直接寻址,使得能在O(1)时间内访问数组中的任意位置.在散列表中,不是直接把关键字作为数组的下标,而是根据关键字计算出相应的下标. 使用散列的查找算法分为两步.第一步是用散列函数将被查找的键转化为数组的一个索引. 我们需要面对两个或多个键都会散列到相同的索引值的情况.因此,第二步就是一个处理碰撞冲突的过程,由两种经典解决碰撞的方法:拉链法和线性探测法. 散列表是算法在时间和空间上作出权衡的经典例子. 如果没有内存限制,我们可以直接

poj_3283 trie树

题目大意 将一副牌进行编号,四种花色分别标记为'C'.'D'.'H'.'S',数值标记为'A'.'1'.'2'.'3'.'4'.'5'.'6'.'7'.'8'.'9'.'10'.'J'.'Q'.'K',则一张牌可以标记为 “数值+花色”,比如 7D, AH, 10S等.     给出N个牌的序列,每个序列视为一条链,每张牌视为链中的一个节点,为了方便存储,可以将具有相同后缀的链聚合在一起.求聚合之后的链中所有节点的个数. 题目分析 具有相同后缀的可以合并在一起,则等价于将每条链翻转之后,具有相同

笔试算法题(39):Trie树(Trie Tree or Prefix Tree)

出题:TRIE树 (Trie Tree or Prefix Tree): 分析: 又称字典树或者前缀树,一种用于快速检索的多叉树结构:英文字母的Trie树为26叉树,数字的Trie树为10叉树:All the descendants of a node have a common prefix of the sequence associated with that node, and the root is associated with the empty sequence. 由于不同的se