[SCOI2016]幸运数字 线性基
题面
题解
题面意思非常明确:求树上一条链的最大异或和。
我们用倍增的思想。
将这条链分成2部分:x ---> lca , lca ---> y
分别求出这2个部分的线性基,然后合并,再求最大异或和。
所以我们现在只需要考虑如何在树上求一条无需拐弯的链的最大线性基。
考虑倍增。
我们预处理出f[i][j]表示从点i开始向上走\(2^j\)构成的链的线性基。至于只有点\(i\)一个点的线性基,我们可以在运算的时候特判一下处理。
暴力预处理后,我们就可以最多 合并3次 + 求LCA的复杂度 求出任意询问的答案了!
如果我们还是跟普通倍增一样暴力向上跳的话,是单次询问\(logn * 60 * 60\),所有询问的总复杂度\(q * logn * 60 * 60\),差不多都到百亿级别了!不知道为什么很多人用这种方法都过了。
所以我们考虑跟RMQ类似的思想,因为在线性基中插入重复串不会有什么影响,因此对于树上任意一条不带拐弯的链,我们完全可以拆分成2段长度至少为一半的链,然后合并即可。
虽然可能会有重复的部分,但是不会有影响。
所以就可以做到单次复杂度\(60 * 60 * 3\)啦,忽略3这个常数复杂度大概是7亿左右,时限6s基本还是可以勉强接受的范围。(这里的60其实是对一个longlong大小的数取log,因为比较大,所以不能忽略)
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 21000
#define ac 40100
int n, m;
int Head[AC], Next[ac], date[ac], tot;
int dep[AC], fa[AC][15];
LL s[AC], ans;
inline LL read()
{
LL x = 0;char c = getchar();
while(c > '9' || c < '0') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
inline void add(int f, int w)
{
date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;
date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot;
}
struct basis{
LL f[61];
inline void clear() {memset(f, 0, sizeof(f));}
inline void ins(LL x)
{
LL maxn = 1LL << 60;
for(R i = 60; ~i; i --, maxn >>= 1)
{
if(!(x & maxn)) continue;
if(!f[i]) {f[i] = x; break;}
else x ^= f[i];
}
}
inline void merge(basis x){for(R i = 0; i <= 60; i ++) ins(x.f[i]);}
}f[AC][15], a, b;
void dfs(int x)
{
f[x][0].ins(s[x]), f[x][0].ins(s[fa[x][0]]);//直接按常规方法处理,使用的时候特判掉
for(R i = 1; i <= 14; i ++)
{
fa[x][i] = fa[fa[x][i - 1]][i - 1];
f[x][i] = f[x][i - 1], f[x][i].merge(f[fa[x][i - 1]][i - 1]);
}
for(R i = Head[x]; i; i = Next[i])
{
int now = date[i];
if(now == fa[x][0]) continue;
fa[now][0] = x, dep[now] = dep[x] + 1, dfs(now);
}
}
inline int LCA(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
for(R i = 14; i >= 0; i --)
if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
for(R i = 14; i >= 0; i --)
if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
if(x == y) return x;
else return fa[x][0];
}
inline int kth(int x, int k)
{
int lim = dep[x] - k;
for(R i = 14; i >= 0; i --)
if(dep[fa[x][i]] >= lim) x = fa[x][i];
return x;
}
void cal(int x, int y)
{
int lca = LCA(x, y), len1 = dep[x] - dep[lca], len2 = dep[y] - dep[lca];
a.clear(), b.clear();
if(!len1) a.ins(s[x]);
else
{
LL tmp = 1, cnt = 0;
for(R i = 0; i <= 60; i ++, tmp <<= 1, ++ cnt) if((tmp << 1) > len1) break;
a = f[x][cnt], a.merge(f[kth(x, len1 - tmp)][cnt]);
}
if(!len2) b.ins(s[y]);
else
{
LL tmp = 1, cnt = 0;
for(R i = 0; i <= 60; i ++, tmp <<= 1, ++ cnt) if((tmp << 1) > len2) break;
b = f[y][cnt], b.merge(f[kth(y, len2 - tmp)][cnt]);
}
a.merge(b);
}
void pre()
{
n = read(), m = read(), fa[1][0] = dep[1] = 1;
for(R i = 1; i <= n; i ++) s[i] = read();
for(R i = 1; i < n; i ++) add(read(), read());
}
void work()
{
for(R i = 1; i <= m; i ++)
{
cal(read(), read()), ans = 0;
for(R j = 60; ~j; j --)
if((ans ^ a.f[j]) > ans) ans ^= a.f[j];
printf("%lld\n", ans);
}
}
int main()
{
// freopen("in.in", "r", stdin);
pre();
dfs(1);//建倍增数组
work();
// fclose(stdin);
return 0;
}
[SCOI2016]幸运数字 线性基的更多相关文章
- BZOJ 4568: [Scoi2016]幸运数字 [线性基 倍增]
4568: [Scoi2016]幸运数字 题意:一颗带点权的树,求树上两点间异或值最大子集的异或值 显然要用线性基 可以用倍增的思想,维护每个点向上\(2^j\)个祖先这些点的线性基,求lca的时候合 ...
- 洛谷P3292 [SCOI2016]幸运数字 线性基+倍增
P3292 [SCOI2016]幸运数字 传送门 题目描述 A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以纪念碑的形式矗立在 ...
- 洛谷P3292 [SCOI2016] 幸运数字 [线性基,倍增]
题目传送门 幸运数字 题目描述 A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的 ...
- bzoj4568 [Scoi2016]幸运数字 线性基+树链剖分
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一.每座城市都有一个 幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征.一些旅行者希望游览 A ...
- P3292 [SCOI2016]幸运数字 线性基
正解:线性基+倍增 解题报告: 先放下传送门QAQ 然后这题,其实没什么太大的技术含量,,,?就几个知识点套在一起,除了代码长以外没任何意义,主要因为想复习下线性基的题目所以还是写下,,, 随便写下思 ...
- BZOJ.4516.[SCOI2016]幸运数字(线性基 点分治)
题目链接 线性基可以\(O(log^2)\)暴力合并.又是树上路径问题,考虑点分治. 对于每个点i求解 LCA(u,v)==i 时的询问(u,v),只需求出这个点到其它点的线性基后,暴力合并. LCA ...
- BZOJ4568: [Scoi2016]幸运数字(线性基 倍增)
题意 题目链接 Sol 线性基是可以合并的 倍增维护一下 然后就做完了?? 喵喵喵? // luogu-judger-enable-o2 #include<bits/stdc++.h> # ...
- BZOJ 4568 [Scoi2016]幸运数字 ——线性基 倍增
[题目分析] 考虑异或的最大值,维护线性基就可以了. 但是有多次的询问,树剖或者倍增都可以. 想了想树剖动辄数百行的代码. 算了,我还是写倍增吧. 注:被位运算和大于号的优先级坑了一次,QaQ [代码 ...
- P3292 [SCOI2016]幸运数字 [线性基+倍增]
线性基+倍增 // by Isaunoya #include <bits/stdc++.h> using namespace std; #define rep(i, x, y) for ( ...
随机推荐
- cogs2554 [福利]可持久化线段树
cogs2554 [福利]可持久化线段树 原题链接 每次修改复制一遍就行了... 1A!!! // It is made by XZZ #include<cstdio> #include& ...
- vscode eslint格式化配置
{ // vscode默认启用了根据文件类型自动设置tabsize的选项 "editor.detectIndentation": false, // 重新设定tabsize &qu ...
- python全栈开发-面向对象-初识2
python_17_day 今日主要内容: 1.类空间,对象空间,查询顺序. 2.组合. 1.类空间,对象空间,查询顺序. class Person: animal = '高级动物' soul = ' ...
- Keil出错解决方法
1.安装KEIL5后创建工程后出现这个报错 解决方法:打开下图目录的文件. Keil.STM32F1xx_DFP.pdsc文件是只读文件,必须将只读属性取消. 如下图所示,注释掉红色圆圈的哪一行,保存 ...
- VIN码/车架号的详解,车架号识别,VIN码识别,OCR车架号识别能带来什么
各位车主在车检时不知道有没有注意到一件事,就是工作人员会打开车前盖在前围钢板上拓一张条码.下面来给大家介绍一下,这张条码就是VIN号,俗称钢印号,就像我们每个人都有自己的身份证号码一样,这也是汽车界的 ...
- google::protobuf 编译方法
这两天用了一下Protobuf 感觉很方便, 记录一下编译过程, 以做务忘(需要安装cmake): 1: 下载地址: https://developers.google.com/protocol-bu ...
- OKHttp使用demo(证书过滤,证书导入,代理访问,文件上传)
此demo需要引入okhttp-3.4.1.jar 和 okio-1.9.0.jar(这两个包需要jdk1.7以上的环境) 对应pom文件是: <dependency> <group ...
- 2.1 Oracle之DML的SQL语句之单表查询以及函数
1.SQL简介 对于不同的数据库来说,SQL语句是相通的,关系型数据库都以SQL语句为操作的标准,只是相应的数据库对应的函数不相同. SQL(Structured Query Language,结构化 ...
- xshell—实现Linux与Windows之间的文件传递
在Windows系统上,通过xshell连接Linux系统. 第一种使用方式:从Linux系统上下载文件到Windows系统. 准备工作: $ sudo apt-get install lrzsz 安 ...
- Python3实现机器学习经典算法(三)ID3决策树
一.ID3决策树概述 ID3决策树是另一种非常重要的用来处理分类问题的结构,它形似一个嵌套N层的IF…ELSE结构,但是它的判断标准不再是一个关系表达式,而是对应的模块的信息增益.它通过信息增益的大小 ...