BZOJ4568 SCOI2016 幸运数字 倍增的思想维护线性基(线性基详解)
2016-07-15 22:44
323 查看
题目大意
给你一颗N个节点的树,每个节点都有一个权值Ai,现在有M组询问,每组询问有3个数u,v,,要求你输出在树上节点u到节点v的路径上,每个节点的权值可以选或不选,求选出的点的权值的异或最大值。N≤2∗104
M≤2∗105
Ai≤260
解题思路
看到这种区间异或最大值的题,那么我们就要联想到线性基,是一个只用维护Log(maxAi)个值就可以表示出区间所有的值的算法,并且合并是Log(maxAi)2。那么如果会线性基后,就可以先预处理出数组F[i][j]表示从i这个节点到以i向上2j个节点的这段区间的线性基,然后对于每个询问,有倍增的思想来合并预处理好的线性基就可以了。
那么现在的问题就是什么是线性基?
线性基
主要思想
线性基是一个主要解决有关异或方面的问题。它的主要思想就是用尽可能少的数表示出集合中的所有数。根据定义我们就可以得出线性基的几个性质。1. 对于集合的每一个数都可以有线性基中的是异或而来。
2. 线性基中的每个数都不可以有线性基中的其他数异或而来。
具体操作
实际上对于每组线性基只需要在二进制上的每一位存一个数就可以了。这个很好理解,这种思想跟高斯消元很像,因为每一个第i位为1的数都可以与集合中其他第i为1的数异或,操作一次后,就只剩下当前数的这一位为1,所以说一个线性基只用存Log(maxAi)个数。合并两个线性基
合并两个线性基时只需把其中一个线性基的数暴力加到另一个中就可以了。加入时的操作也跟高斯消元类似,我们首先需要判断的就是当前这个数能否被另一个线性基中的数表示。加入当前待加入的这个数的第i位为1,那么我们就要判断另一个线性基中的第i位有没有存下数字。如果有那么这一位就可以被消去,如果没有那么这个数就不能被另一个线性基表示,所以在第i位加入这个数。所以合并两个线性基的复杂度是O(Log(maxAi)2)的。程序
//YxuanwKeith #include <cstring> #include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int MAXN = 2e4 + 5; LL F[MAXN][16][65], Ans[65]; int N, M, Deep[MAXN], Fa[MAXN][16]; int tot, Last[MAXN], Next[MAXN * 2], Go[MAXN * 2]; void Link(int u, int v) { Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v; } void Merge(LL *F, LL Now) { for (int i = 60; i + 1; i --) { if ((Now >> i) & 1ll) { if (!F[i]) { F[i] = Now; return; } else Now ^= F[i]; } } } void MergeAll(LL *F, LL *G) { for (int i = 60; i + 1; i --) if (G[i]) Merge(F, G[i]); } void Dfs(int Now, int Pre) { Deep[Now] = Deep[Pre] + 1; Fa[Now][0] = Pre; for (int p = Last[Now]; p; p = Next[p]) if (Go[p] != Pre) Dfs(Go[p], Now); } void Prepare() { for (int j = 1; j <= 15; j ++) for (int i = 1; i <= N; i ++) { Fa[i][j] = Fa[Fa[i][j - 1]][j - 1]; memcpy(F[i][j], F[i][j - 1], sizeof F[i][j - 1]); MergeAll(F[i][j], F[Fa[i][j - 1]][j - 1]); } } void Lca(int x, int y) { if (Deep[x] < Deep[y]) swap(x, y); for (int i = 15; i + 1; i --) if (Deep[Fa[x][i]] >= Deep[y]) { MergeAll(Ans, F[x][i]); x = Fa[x][i]; } if (x == y) { MergeAll(Ans, F[x][0]); return; } for (int i = 15; i + 1; i --) if (Fa[x][i] != Fa[y][i]) { MergeAll(Ans, F[x][i]), MergeAll(Ans, F[y][i]); x = Fa[x][i], y = Fa[y][i]; } MergeAll(Ans, F[x][0]), MergeAll(Ans, F[y][0]); MergeAll(Ans, F[Fa[x][0]][0]); } void Solve() { for (int i = 1; i <= M; i ++) { memset(Ans, 0, sizeof Ans); int u, v; scanf("%d%d", &u, &v); Lca(u, v); LL Sum = 0; for (int j = 60; j + 1; j --) Sum = max(Sum, Sum ^ Ans[j]); printf("%lld\n", Sum); } } int main() { scanf("%d%d", &N, &M); for (int i = 1; i <= N; i ++) { LL Now; scanf("%lld", &Now); Merge(F[i][0], Now); } for (int i = 1; i < N; i ++) { int u, v; scanf("%d%d", &u, &v); Link(u, v), Link(v, u); } Dfs(1, 0); Prepare(); Solve(); }
相关文章推荐
- nginx打开目录浏览功能
- 全面梳理TheCthulhu网站公布的那些泄露数据,并扒一扒TheCthulhu其人 很多大库下载.
- CodeForces 586B Laurenty and Shop
- 决心书和自我介绍224-陈一鹏 杭州
- 深入理解HDFS:Hadoop分布式文件系统
- Nginx系列(一)--nginx是什么?
- [linux小技巧]
- 赋予option元素点击事件后,点击select时却触发了option事件。如何解决?
- 网络基本功(十三):细说Linux网络配置(下)
- 网络基本功(十二):细说Linux网络配置(上)
- 运维日记001-在虚拟机上安装RHEL6/CentOS6实验环境模板
- [BX]和loop指令
- shell脚本 - 学习if语句和变量赋值
- linux下的free命令和top命令
- Mac搭建nginx+rtmp服务器
- Tomcat9源码编译及导入Eclipse
- 【OpenCV笔记 08】OpenCV中分离颜色通道split()和图像通道混合merge()
- Linux find命令
- Windows平台下载和配置Apache2.4
- shell编程详解(二)