【bzoj2588/P2633】count on a tree —— LCA + 主席树
(以下是luogu题面)
题目描述
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
输入输出格式
输入格式:
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。
输出格式:
M行,表示每个询问的答案。
说明
HINT:
N,M<=100000
思路:
一看到静态第K小,你就要想到主席树;一看到树,你就要树链剖分。
的确用树剖把树搞成区间,在上面架主席树是比较直接的想法。问题是树剖把链所划分成的区间的数目是不确定的,因此树剖能维护的一般是能够对每条链独立求出,再用结合律结合得出答案的信息。但是主席树需要用确定多的几棵前缀权值树维护一个虚拟的树,查询函数每次传入的参数最好是一样多的。我猜树剖可以写,但是实在太麻烦,而且还多一个log。
我们需要维护的其实只是两点之间简单路径的信息,联想到用树上差分实现树链修改的过程,我们可以用主席树维护一个类似于前缀的东西:定义root[i]表示从原树根节点到点i的路径上的权值线段树的根。每棵新树基于的原始版本是它父亲u的那棵树。这个思想与区间前缀和的关系就好比字符串之于Trie树(可能这么比喻也不恰当)。按dfs序建立这样的主席树之后,我们查询的树上路径可以这样求出:
设S[l, r]维护从l到r路径的值域信息的线段树,则
S[u, v] = S[root, u] + S[root, v] - S[root, lca(u, v)] - S[root, father(lca(u, v))]
可以看到这个形式与树上节点信息差分很像。那么我们还需要维护LCA的查询。(终于可以用树剖辣!@w@……不,你不想)
(倍增大法好
- #include <cstdio>
- #include <iostream>
- #include <cstring>
- #include <algorithm>
- #include <cctype>
- #define BUG puts("$$$")
- #define LG 17
- #define maxn 100010
- template <class T>
- void read(T &x) {
- x = 0;
- char ch = getchar();
- while (!isdigit(ch))
- ch = getchar();
- while (isdigit(ch)) {
- x = x * 10 + (ch ^ 48);
- ch = getchar();
- }
- }
- using namespace std;
- struct E {
- int to, nxt;
- } edge[maxn << 1];
- int head[maxn], top;
- int n, m;
- int a[maxn], N, st[maxn];
- inline void insert(int u, int v) {
- edge[++top] = (E) {v, head[u]};
- head[u] = top;
- }
- namespace LCA {
- int f[LG + 2][maxn], d[maxn];
- void dfs(int u, int pre) {
- d[u] = d[pre] + 1;
- f[0][u] = pre;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v != pre)
- dfs(v, u);
- }
- }
- void init1() {
- dfs(1, 0);
- for (int k = 1; k <= LG; ++k)
- for (int i = 1; i <= n; ++i)
- f[k][i] = f[k-1][f[k-1][i]];
- }
- void swim(int &x, int d) {
- for (int i = 0; d; d >>= 1, ++i)
- if (d & 1)
- x = f[i][x];
- }
- int find(int u, int v) {
- if (d[u] > d[v]) swap(u, v);
- swim(v, d[v] - d[u]);
- if (u == v)
- return u;
- for (int k = LG; k >= 0; --k)
- if (f[k][u] != f[k][v])
- u = f[k][u], v = f[k][v];
- return f[0][u];
- }
- } using namespace LCA;
- namespace President_tree {
- #define lc(i) seg[i].lc
- #define rc(i) seg[i].rc
- #define mid ((l + r) >> 1)
- int root[maxn], tot;
- struct node {
- int cnt, lc, rc;
- } seg[maxn * 30];
- inline void update(int nd) {
- seg[nd].cnt = seg[lc(nd)].cnt + seg[rc(nd)].cnt;
- }
- int build(int l, int r) {
- int nd = ++tot;
- seg[nd].cnt = 0;
- if (l == r)
- return nd;
- lc(nd) = build(l, mid);
- rc(nd) = build(mid + 1, r);
- return nd;
- }
- int modify(int pre, int l, int r, int x) {
- int nd = ++tot;
- seg[nd] = seg[pre];
- if (l == r) {
- ++seg[nd].cnt;
- return nd;
- }
- if (x <= mid)
- lc(nd) = modify(lc(pre), l, mid, x);
- else rc(nd) = modify(rc(pre), mid + 1, r, x);
- update(nd);
- return nd;
- }
- void init2(int u, int pre) {
- root[u] = modify(root[pre], 1, N, a[u]);
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v != pre)
- init2(v, u);
- }
- }
- int query(int u, int v, int lca, int flca, int l, int r, int k) {
- if (l == r) {
- return st[l];
- }
- int lsum = seg[lc(u)].cnt + seg[lc(v)].cnt - seg[lc(lca)].cnt - seg[lc(flca)].cnt;
- if (k <= lsum)
- return query(lc(u), lc(v), lc(lca), lc(flca), l, mid, k);
- return query(rc(u), rc(v), rc(lca), rc(flca), mid + 1, r, k - lsum);
- }
- } using namespace President_tree;
- int contra(int* a) {
- for (int i = 1; i <= n; ++i)
- st[i] = a[i];
- sort(st + 1, st + 1 + n);
- int len = unique(st + 1, st + 1 + n) - st - 1;
- for (int i = 1; i <= n; ++i)
- a[i] = lower_bound(st + 1, st + len + 1, a[i]) - st;
- return len;
- }
- int main() {
- read(n), read(m);
- int u, v;
- for (int i = 1; i <= n; ++i)
- read(a[i]);
- N = contra(a);
- for (int i = 1; i < n; ++i) {
- read(u), read(v);
- insert(u, v), insert(v, u);
- }
- init1();
- init2(1, 0);
- int k, ans = 0;
- for (int i = 1; i <= m; ++i) {
- read(u), read(v), read(k);
- u = ans xor u;
- int lca = find(u, v);
- ans = query(root[u], root[v], root[lca], root[f[0][lca]], 1, N, k);
- printf("%d\n", ans);
- }
- return 0;
- }
【bzoj2588/P2633】count on a tree —— LCA + 主席树的更多相关文章
- 【BZOJ2588】Count On a Tree(主席树)
[BZOJ2588]Count On a Tree(主席树) 题面 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第 ...
- [BZOJ2588]Count on a tree(LCA+主席树)
题面 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问 ...
- 洛谷P2633 Count on a tree(主席树,倍增LCA)
洛谷题目传送门 题目大意 就是给你一棵树,每个点都有点权,每次任意询问两点间路径上点权第k小的值(强制在线). 思路分析 第k小......又是主席树了.但这次变成树了,无法直接维护前缀和. 又是树上 ...
- 洛谷P2633 Count on a tree(主席树,倍增LCA,树上差分)
洛谷题目传送门 题目大意 就是给你一棵树,每个点都有点权,每次任意询问两点间路径上点权第k小的值(强制在线). 思路分析 第k小......又是主席树了.但这次变成树了,无法直接维护前缀和. 又是树上 ...
- BZOJ2588 SPOJ10628 Count on a tree 【主席树】
BZOJ2588 Count on a tree 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中l ...
- BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )
Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...
- Count on a tree 树上主席树
Count on a tree 树上主席树 给\(n\)个树,每个点有点权,每次询问\(u,v\)路径上第\(k\)小点权,强制在线 求解区间静态第\(k\)小即用主席树. 树上主席树类似于区间上主席 ...
- [Bzoj2588]Count on a tree(主席树+LCA)
Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始 ...
- 【洛谷 P2633】 Count on a tree(主席树,树上差分)
题目链接 思维难度0 实现难度7 建出主席树后用两点的状态减去lca和lca父亲的状态,然后在新树上跑第\(k\)小 #include <cstdio> #include <cstr ...
随机推荐
- F. Make It Connected 解析(思維、MST)
Codeforce 1095 F. Make It Connected 解析(思維.MST) 今天我們來看看CF1095F 題目連結 題目 給你\(n\)個點,每個點\(u\)還有一個值\(a[u]\ ...
- DevOps 视角的前后端分离与实战
本文作者:CODING - 廖红坤 前言 随着微前端.微服务等技术理念和架构的蓬勃发展,我们已经没必要去讨论为什么要前后端分离这种话题,前后端分离已成为互联网项目开发的标准模式.前后端在各自的领域发展 ...
- [Luogu P4124] [CQOI2016]手机号码 (数位DP)
题面 传送门:洛咕 Solution 感谢神仙@lizbaka的教学 这题是数位DP的非常非常模板的题目,只是状态有点多 . 这题我使用记忆化搜索实现的 中国有句古话说的好,有多少个要求就设多少个状态 ...
- 令人惊叹的百度Echarts,大数据分析的必备工具
学习目录 1.可视化面板介绍 1.1技术要点 1.2案例适配方案 1.3页面主体布局2.Echarts(重点) 2.1echarts介绍 2.2echarts体 ...
- .netcore3.1使用log4net/nlog记录日志
.netcore3.1使用log4net/nlog记录日志 .netcore3.1与2.x之间很是有不少差异的.本来想通过ctrl+c,ctrl+v将在2.2中实现的简单日志记录搬到.netcore3 ...
- 聊聊Go代码覆盖率技术与最佳实践
"聊点干货" 覆盖率技术基础 截止到Go1.15.2以前,关于覆盖率技术底层实现,以下知识点您应该知道: go语言采用的是插桩源码的形式,而不是待二进制执行时再去设置breakpo ...
- C++ 基础 3:类和对象
1 类和对象 1.1 类定义 类定义是以关键字 class 开头,后跟类的名称.类的主体是包含在一对花括号中.类定义后必须跟着一个分号或一个声明列表.例如,我们使用关键字 class 定义 Box 数 ...
- WSL-Ubuntu18.04 磁盘迁移 与 ns3-gym 安装
WSL 安装 win10 版本应大于或等于 1903 win10 设置页面 输入 控制面板 并点击进入 找到 程序和功能 并打开 找到 启动或关闭 Windows 功能 并打开 向下拉 勾选 适用于L ...
- tp6.0.x 反序列化漏洞
tp6 反序列化漏洞复现 环境 tp6.0 apache php7.3 漏洞分析 反序列化漏洞需要存在 unserialize() 作为触发条件,修改入口文件 app/controller/Index ...
- tcpack--3快速确认模式
接收到数据报后,会调用tcp_event_data_recv(),不管是在慢速路径的tcp_data_queue中调用还是 在快速路径中处理接收数据后直接调用,注意(如果len <= tcp_h ...