当前位置: 首页 > news >正文

SPOJ COT3.Combat on a tree(博弈论 Trie合并)

题目链接


\(Description\)

给定一棵\(n\)个点的树,每个点是黑色或白色。两个人轮流操作,每次可以选一个白色的点,将它到根节点路径上的所有点染黑。不能操作的人输,求先手是否能赢。如果能,输出第一步选择哪些节点能赢。
\(n\leq10^5\)

\(Solution\)

Orz huzecong。

对于叶子节点,如果能染色,\(SG(x)=1\),否则\(=0\)
考虑从下往上算每棵子树的\(SG\)值。设\(SG(x)\)表示\(x\)子树的\(SG\)值,\(g(x)\)表示对\(x\)这棵子树操作能得到的后继的\(SG\)值集合(只考虑\(x\)子树),那么\(SG(x)=\mathbb{mex}\{g(x)\}\)

考虑如何计算\(g(x)\)。令\(sum[x]=sg(v_1)\ \mathbb{xor}\ sg(v_2)\ \mathbb{xor}...,\ v_i\in son[x]\)
\(x\)是黑点,假设这次操作选了\(v_i\)子树中的某个点,那么其它子树状态不变,\(v_i\)子树的后继状态会变成\(g(v_i)\)中的某个,所以\(g(x)=sum[x]\ \mathbb{xor}\ sg(v_i)\ \mathbb{xor}\ (g(v_i)中的某个值)\)
把子树内每个\(g(v_i)\)整体\(\mathbb{xor}\)一个数,合并起来,就可以得到\(g(x)\)了。
\(x\)是白点,多了一种选\(x\)的后继,选\(x\)后得到状态的\(SG\)值就是\(sum[x]\)。所以在\(g(x)\)中再插入一个\(sum[x]\)即可。

还要支持求\(\mathbb{mex}\),可以用\(01Trie\)维护。合并的时候可以启发式合并,\(O(n\log^2n)\),也可以类似线段树合并做到\(O(n\log n)\)

对于第二问,考虑选择一个节点后局面的\(SG\)值。容易发现就是除去它到根节点路径上的点的所有点的\(SG\)值的异或和。
\(up[x]\)表示除去\(x\)到根节点路径上的点外,所有节点的\(SG\)值异或和,那么\(up[x]=up[fa[x]]^{\wedge}sum[fa[x]]^{\wedge}SG(x)\)。选择\(x\)\(x\)的各棵子树是独立的,局面的\(SG\)值就是\(up[x]^{\wedge}sum[x]\)\(up\)也可以直接\(DFS\)的时候传个参)。
所以如果\(up[x]^{\wedge}sum[x]=0\),选这个点就是必胜的。

这个\(SG\)值的最大值是啥啊...
注意\(Trie\)要特判叶子的地方(尤其是Merge,注意像线段树合并一样判下叶子)(我怎么老是写错...)。


//0.11s 35M
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define Bit 16
#define gc() getchar()
#define MAXIN 500000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;

int Enum,H[N],nxt[N<<1],to[N<<1],root[N],sg[N],sum[N],up[N];
bool col[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Trie
{
    #define S N*20
    #define ls son[x][0]
    #define rs son[x][1]
    int tot,son[S][2],tag[S];
    bool full[S];
    #define Update(x) full[x]=full[ls]&&full[rs]
    inline void Upd(int x,int v,int dep)
    {
//      if(dep<0) return;
        if(v>>dep&1) std::swap(ls,rs);
        tag[x]^=v;
    }
    inline void PushDown(int x,int dep)
    {
        if(dep&&tag[x]) Upd(ls,tag[x],dep-1), Upd(rs,tag[x],dep-1), tag[x]=0;
    }
    void Insert(int &x,int v,int dep)
    {
        x=++tot;
        if(dep<0) {full[x]=1; return;}
        v>>dep&1 ? Insert(rs,v,dep-1) : Insert(ls,v,dep-1);
    }
    int Merge(int x,int y,int dep)
    {
        if(!x||!y) return x|y;
        if(dep<0) return x;
        PushDown(x,dep), PushDown(y,dep);
        ls=Merge(ls,son[y][0],dep-1), rs=Merge(rs,son[y][1],dep-1);
        Update(x); return x;
    }
    int Mex(int x,int dep)
    {
        if(!x||dep<0) return 0;
        PushDown(x,dep);
        return full[ls]?(1<<dep)+Mex(rs,dep-1):Mex(ls,dep-1);
    }
}T;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now;
}
inline void AE(int u,int v)
{
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
void DFS1(int x,int fa)
{
    if(!col[x]) T.Insert(root[x],0,Bit);
    int s=0;
    for(int i=H[x],v; i; i=nxt[i])
        if((v=to[i])!=fa)
            DFS1(v,x), s^=sg[v], T.Upd(root[v],sg[v],Bit), root[x]=T.Merge(root[x],root[v],Bit);
    if(s) T.Upd(root[x],s,Bit);
    sum[x]=s, sg[x]=T.Mex(root[x],Bit);
}
void DFS2(int x,int fa)
{
    up[x]=up[fa]^sum[fa]^sg[x];
    for(int i=H[x],v; i; i=nxt[i])
        if((v=to[i])!=fa) DFS2(v,x);
}

int main()
{
    const int n=read();
    for(int i=1; i<=n; ++i) col[i]=read();
    for(int i=1; i<n; ++i) AE(read(),read());
    DFS1(1,0), sum[0]=sg[1], DFS2(1,0);
    bool f=0;
    for(int x=1; x<=n; ++x) if(!col[x]&&!(sum[x]^up[x])) f=1, printf("%d\n",x);
    if(!f) puts("-1");

    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10555664.html

相关文章:

  • HDU 2883 kebab
  • C++学习笔记30,指针的引用(2)
  • fatal error C1010: 在查找预编译头时遇到意外的文件结尾
  • c# Winform dev控件之ChartControl
  • Spring框架学习07——基于传统代理类的AOP实现
  • html迪士尼网页实现代码
  • HDU 2159 FATE
  • es 基于match_phrase/fuzzy的模糊匹配原理及使用
  • spring_事務
  • ASP.NET Core OData now Available
  • 01背包 完全背包 算法解析
  • 解决任务计划程序未启动任务,因为相同任务的实例正在运行的问题
  • 关于oracle的一些函数(数字、字符、转换、空值)
  • 安卓开发笔记(十五):编写一款能够查看任意网站源代码的应用程序
  • Spring拓展接口之FactoryBean,我们来看看其源码实现
  • 【笔记】你不知道的JS读书笔记——Promise
  • 03Go 类型总结
  • co.js - 让异步代码同步化
  • crontab执行失败的多种原因
  • orm2 中文文档 3.1 模型属性
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • SpingCloudBus整合RabbitMQ
  • tensorflow学习笔记3——MNIST应用篇
  • 第2章 网络文档
  • 动态魔术使用DBMS_SQL
  • 技术发展面试
  • 思考 CSS 架构
  • 我感觉这是史上最牛的防sql注入方法类
  • 一个完整Java Web项目背后的密码
  • 译自由幺半群
  • 终端用户监控:真实用户监控还是模拟监控?
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • #DBA杂记1
  • (02)vite环境变量配置
  • (14)Hive调优——合并小文件
  • (3)llvm ir转换过程
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (差分)胡桃爱原石
  • (二)Linux——Linux常用指令
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (六)c52学习之旅-独立按键
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • .net 4.0发布后不能正常显示图片问题
  • .net core 依赖注入的基本用发
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • @Documented注解的作用
  • @ModelAttribute注解使用
  • [].shift.call( arguments ) 和 [].slice.call( arguments )
  • [ai笔记9] openAI Sora技术文档引用文献汇总