Trie树合并 + SG函数 ---- BZOJ4730. Alice和Bob又在玩游戏(动态开点Trie 树上全局异或标记 + 合并 + 博弈论)
题目大题
题目大意:
解题思路:
- 首先我们对于子树u的SG函数为SG函数为SG函数为
⨁是异或和\bigoplus是异或和⨁是异或和
SG[u]=mex{⨁w∈(w的父亲在u到v的路径上)SG[w]∣v∈(u的子树里面的点)}SG[u]=mex\{\bigoplus_{w\in(w的父亲在u到v的路径上)}SG[w]|v\in(u的子树里面的点)\}SG[u]=mex{w∈(w的父亲在u到v的路径上)⨁SG[w]∣v∈(u的子树里面的点)}
通俗一点就是就是枚举uuu所有的里面的子树节点vvv,然后把分裂出来的子树www的SGSGSG函数⊕\oplus⊕起来就好了
但是这样的复杂度是O(n3)O(n^3)O(n3)的,我们想办法优化一下?
- 我们看这个SG函数SG函数SG函数是自底向上跟新的,那么我们从底向上考虑:
- 我们定义
au=⨁v是u的直接儿子SG[v]a_u=\bigoplus_{v是u的直接儿子}SG[v]au=v是u的直接儿子⨁SG[v]
bu=⨁v是u直接儿子并且包括uSG[v]b_u=\bigoplus_{v是u直接儿子并且包括u}SG[v]bu=v是u直接儿子并且包括u⨁SG[v]
只考虑相邻两层
那么上面的公式就可以变成:根据异或的消去律 对于一个vvv
au⊕(⨁w是u和v的路径上面的点&&w不等于vbw)a_u\;\oplus\;(\bigoplus_{w是u和v的路径上面的点\&\&w不等于v}b_w)au⊕(w是u和v的路径上面的点&&w不等于v⨁bw)
手模一下你们发现就只剩下点1,2,3,4了1,2,3,4了1,2,3,4了就刚好是
那么式子就成了。
⨁w∈(w的父亲在u到v的路径上)SG[w]=(⨁w是u和v的路径上面的点&&w不等于vbw)⊕au\bigoplus_{w\in(w的父亲在u到v的路径上)}SG[w]=(\bigoplus_{w是u和v的路径上面的点\&\&w不等于v}b_w)\oplus a_uw∈(w的父亲在u到v的路径上)⨁SG[w]=(w是u和v的路径上面的点&&w不等于v⨁bw)⊕au
假设有一棵二进制意义下的 Trie 树,其中储存了所有 uuu 的子树内的 vvv 对应的⨁w是u和v的路径上面的点bw\bigoplus_{w是u和v的路径上面的点}b_w⨁w是u和v的路径上面的点bw.
易得我们需要求得一个最小的数 resresres ,使得 aua_uau异或上 Trie 树储存的任意一个数都不能得到resresres.
考虑在 Trie 树上从上往下贪心(从高到低确定 resresres的每一位):设当前要确定第 iii 位(这里二进制位由低往高,最低位为第 000 位),现在走到 Trie 树的节点 xxx当 aua_uau的第 iii 位为 111 的时候,我们考虑 resr e sres 的第 iii 位可以为 000 的条件:
xxx 的右子节点(由字符为 111 的边转移到的子节点)对应的子树不是满二叉树,即子树内的叶子数小于 2i2^i2i可以在 Trie 的每个节点上储存(一个 sizesizesize 表示子树内的叶子个数)这时候我们就可以把resr e sres 的第 iii 位设为 000 ,否则设为 111
aua_uau的第 iii 位为 000 同理,当 xxx 到达了叶子节点之后就得到了 SG[u]=resS G [ u ] = r e sSG[u]=res
但我们显然不能直接把这棵 Trie 建出来,否则这个 DP 不能得到复杂度上的优化
我们考虑一下父子关系:就是自顶向上合并的时候我们发现⨁w是u和v的路径上面的点&&w不等于vbw\bigoplus_{w是u和v的路径上面的点\&\&w不等于v}b_w⨁w是u和v的路径上面的点&&w不等于vbw里面w不等于vw不等于vw不等于v那么我们向上时候可以把(bw[w=v]=SG[v]⊕au)异或上去(b_w[w=v]=SG[v]\oplus a_u)异或上去(bw[w=v]=SG[v]⊕au)异或上去然后再把所有的fa[v]fa[v]fa[v]的所有儿子节点的字典树都合并起来就可以了。
这个你手模一下父子关系就可以发现了
这里是全局的异或SG[v]⊕auSG[v]\oplus a_uSG[v]⊕au,我们要在字典树上面打标记类似线段树吧很好理解,每次递归时候下传就可以了
下放标记的具体方法:如果要下放 Trie 树节点 xxx上的标记 tagt a gtag ,且连接 xxx 与 xxx 的子节点的转移边表示第 iii 位,则把 xxx 的左子节点的标记和右子节点的标记都异或上 tagt a gtag如果 tagt a gtag 的第 iii 位为 111 则需要交换 xxx 的左右子节点(因为这时异或上 tagt a gtag相当于第 iii 位被取反),最后将 xxx 节点上的标记清空复杂度 O(Tnlogn)O ( T n log n )O(Tnlogn)这么为了不能全局动态开点,我们可以先在里面插入一个000
AC code
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 7e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {read(first);read(args...);
}
int n, m, tot;
int fa[N];
vector<int> G[N];struct Trie {int lc, rc, tag, sze;void init() {lc = rc = tag = sze = 0;}
}T[N];
int SG[N], rt[N];
inline int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);
}
inline void init() {for(int i = 1; i <= n; ++ i) fa[i] = i, G[i].clear();tot = 0;
}void downdate(int x, int i) {if((T[x].tag>>i)&1) swap(T[x].lc,T[x].rc);if(T[x].lc) T[T[x].lc].tag ^= T[x].tag;if(T[x].rc) T[T[x].rc].tag ^= T[x].tag;
}int merge(int i, int x, int y) {if(!x || !y) return x + y;if(i == -1) return x;downdate(x,i), downdate(y,i);T[x].lc = merge(i-1,T[x].lc,T[y].lc);T[x].rc = merge(i-1,T[x].rc,T[y].rc);T[x].sze = T[T[x].lc].sze + T[T[x].rc].sze;return x;
}int min_mex_xor(int x, int num) {int res = 0;for(int i = 30; i >= 0; -- i) {downdate(x,i);if(num >> i & 1) {if(T[T[x].rc].sze < (1 << i)) x = T[x].rc;else x = T[x].lc, res |= 1 << i;} else {if(T[T[x].lc].sze < (1 << i)) x = T[x].lc;else x = T[x].rc, res |= 1 << i;}}return res;
}void ins(int i, int &x, int num ) { if(!x) T[x = ++ tot].init();if(i == -1) return (void) (T[x].sze = 1);downdate(x,i);if(num >> i & 1) ins(i-1,T[x].rc,num);else ins(i-1,T[x].lc,num);T[x].sze = T[T[x].lc].sze + T[T[x].rc].sze;
}inline int dfs(int u, int fa) {int au = 0;rt[u] = 0;ins(30,rt[u],0);for(auto it : G[u]) {if(it == fa) continue;dfs(it,u);merge(30,rt[u],rt[it]);au ^= SG[it];}SG[u] = min_mex_xor(rt[u],au);T[rt[u]].tag ^= (au ^ SG[u]);return SG[u];
}int main() {IOS;int _;cin >> _;while(_--) {cin >> n >> m;init();for(int i = 1; i <= m; ++ i) {int u, v;cin >> u >> v;G[u].push_back(v);G[v].push_back(u);int fu = find(u), fv = find(v);if(fu != fv) {if(fu > fv) swap(fu,fv);fa[fv] = fu;}}int ans = 0;for(int i = 1; i <= n; ++ i) if(find(i) == i) ans ^= dfs(i,0);if(ans) cout << "Alice\n";else cout << "Bob\n";}
}
Trie树合并 + SG函数 ---- BZOJ4730. Alice和Bob又在玩游戏(动态开点Trie 树上全局异或标记 + 合并 + 博弈论)相关推荐
- 【清华集训2016】Alice和Bob又在玩游戏
不难的题目.因为SG性质,所以只需要对一棵树求出. 然后如果发现从上往下DP不太行,所以从下往上DP. 考虑一个点对子树的合并,考虑下一个删的点在哪一个子树,那么剩下的状态实际上就是把一个子树所有能达 ...
- 一类SG函数递推性质的深入分析——2018ACM陕西邀请赛H题
题目描述 定义一种有根二叉树\(T(n)\)如下: (1)\(T(1)\)是一条长度为\(p\)的链: (2)\(T(2)\)是一条长度为\(q\)的链: (3)\(T(i)\)是一棵二叉树,它的左子 ...
- 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)
图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...
- Trie 树(数据结构)
1.Trie树的概念 Trie树是数据结构比较简单的一种.Trie 树的基本用法是高效的存储和查找字符串集合的数据结构.Trie树也叫做字典树,它是一个树形结构.是一种专门处理字符串匹配的数据结构,用 ...
- Trie 树——搜索关键词提示
当你在搜索引擎中输入想要搜索的一部分内容时,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,这个功能是怎么实现的呢?其实底层最基本的就是 Trie 树这种数据结构. 1. 什么是 "Tri ...
- 海量路由表能够使用HASH表存储吗-HASH查找和TRIE树查找
千万别! 非常多人这样说,也包括我. Linux内核早就把HASH路由表去掉了.如今就仅仅剩下TRIE了,只是我还是希望就这两种数据结构展开一些形而上的讨论. 1.hash和trie/radix ha ...
- Trie 树——搜索关键词提示 1
当你在搜索引擎中输入想要搜索的一部分内容时,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,这个功能是怎么实现的呢?其实底层最基本的就是 Trie 树这种数据结构. 1. 什么是 "Tri ...
- 字符串匹配算法(Trie树)
文章目录 1. Trie树概念 2. Trie树操作 2.1 存储 2.2 查找 2.3 插入 2.4 删除 2.5 打印 3. 完整代码 4. Trie树与散列表.红黑树的比较 4.1 思考题 参考 ...
- 【BZOJ10561862】【codevs1985】排名系统,Splay+trie树
传送门1 传送门2 传送门3 写在前面:我就是不用hash! 思路: 听Shallwe说这道题卡内存要用hash,表示不服开始写trie树 比较关键的是建立trie树节点,Splay节点和字符串(名字 ...
最新文章
- k8s service type_k8s重器之Service
- 6G网络智能内生的思考
- 第一讲 ODE几何方法
- linux内核模块编译出现找不到include/generated/asm/unistd_32.h” 问题解决
- Linux磁盘分区及文件系统管理之基础概念
- Chrome的一点小问题
- linux添加虚拟硬盘命令,虚拟机linux扩盘命令操作
- 数据库原理及应用【六】数据库设计
- NS2:undefined reference to `xxx' collect2: error: ld returned 1 exit status
- 立即表达式的多种写法与注意点以及in操作符的作用
- 今日博文视点大咖直播伴你读No.3:数据分析学习之道
- 快逸报表研究-冻结表头
- 转速恒压频比交流变频调速系统Simulink仿真,可观察到电压频率的变比情况以及电动机的转速波形。
- Java POI 导出 Excel 单元格 合并单元格 相邻的相同值 合并
- C语言使用getch()读取方向键
- 龙芯2号处理器,龙芯2K1000芯片参数
- 硬盘坏了数据可以恢复吗?
- VUE饿了么学习笔记(6)goods界面滚动和点击联动的实现
- 安装MongoDB报错Verify that you have sufficient privileges to start system services的解决方法
- OpenGL-立方体贴图之天空盒
热门文章
- selenium之简单使用
- 计算机网络_NAT与NAPT
- ACMNO.49:一元三次方程求解(主要就是精度问题)
- 理解CNN卷积层与池化层计算
- 【好资源】473页斯坦福数学基础:《应用线性代数》(附pdf和ppt下载)
- 基于Opencv的图像单应性转换实战
- Flutter使用CupertinoAlertDialog 报 'alertDialogLabel' was called on null.
- javascript与java正则表达式写法的区别
- 【Unity_UWP】Unity 工程发布win10 UWP 时的本地文件读取 (上篇)
- Node.js连接MySQL