「luogu2633」Count on a tree

「luogu2633」Count on a tree

传送门
树上主席树板子。
每个节点的根从其父节点更新得到,查询的时候差分一下就好了。
参考代码:

#include <algorithm>
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 1e5 + 5;

int tot, head[_], nxt[_ << 1], ver[_ << 1];
inline void Add_edge(int u, int v)
{ nxt[++tot] = head[u], head[u] = tot, ver[tot] = v; }

int n, q, a[_], X[_];
int dep[_], siz[_], son[_], fa[_], top[_];
int tt, rt[_], lc[_ << 5], rc[_ << 5], cnt[_ << 5];

inline void update(int& p, int q, int v, int l = 1, int r = X[0]) {
    p = ++tt, lc[p] = lc[q], rc[p] = rc[q], cnt[p] = cnt[q] + 1;
    if (l == r) return ;
    int mid = (l + r) >> 1;
    if (v <= mid) update(lc[p], lc[q], v, l, mid);
    else update(rc[p], rc[q], v, mid + 1, r);
}

inline int query(int u, int v, int lca, int flca, int k, int l = 1, int r = X[0]) {
    if (l == r) return l;
    int mid = (l + r) >> 1;
    int num = cnt[lc[u]] + cnt[lc[v]] - cnt[lc[lca]] - cnt[lc[flca]];
    if (num >= k) return query(lc[u], lc[v], lc[lca], lc[flca], k, l, mid);
    else return query(rc[u], rc[v], rc[lca], rc[flca], k - num, mid + 1, r);
}

inline void dfs(int u, int f) {
    dep[u] = dep[f] + 1, siz[u] = 1, fa[u] = f;
    update(rt[u], rt[f], a[u]);
    for (rg int i = head[u]; i; i = nxt[i]) {
        int v = ver[i]; if (v == f) continue ;
        dfs(v, u), siz[u] += siz[v];
        if (siz[son[u]] < siz[v]) son[u] = v;
    }
}

inline void dfs(int u, int f, int topf) {
    top[u] = topf;
    if (son[u]) dfs(son[u], u, topf);
    for (rg int i = head[u]; i; i = nxt[i]) {
        int v = ver[i]; if (v == f || v == son[u]) continue ;
        dfs(v, u, v);
    }
}

inline int LCA(int x, int y) {
    int fx = top[x], fy = top[y];
    while (fx != fy) {
        if (dep[fx] < dep[fy]) swap(fx, fy), swap(x, y);
        x = fa[fx], fx = top[x];
    }
    return dep[x] < dep[y] ? x : y;
}

int main() {
    read(n), read(q);
    for (rg int i = 1; i <= n; ++i) read(a[i]), X[i] = a[i];
    sort(X + 1, X + n + 1);
    X[0] = unique(X + 1, X + n + 1) - X - 1;
    for (rg int i = 1; i <= n; ++i) a[i] = lower_bound(X + 1, X + X[0] + 1, a[i]) - X;
    for (rg int u, v, i = 1; i < n; ++i)
        read(u), read(v), Add_edge(u, v), Add_edge(v, u);
    dfs(1, 0), dfs(1, 0, 1);
    for (rg int ans = 0, u, v, k, lca; q--; ) {
        read(u), u ^= ans, read(v), read(k), lca = LCA(u, v);
        ans = X[query(rt[u], rt[v], rt[lca], rt[fa[lca]], k)], printf("%d\n", ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zsbzsb/p/12231655.html

时间: 2024-11-07 18:08:50

「luogu2633」Count on a tree的相关文章

「SPOJ10707」Count on a tree II

「SPOJ10707」Count on a tree II 传送门 树上莫队板子题. 锻炼基础,没什么好说的. 参考代码: #include <algorithm> #include <cstdio> #include <cmath> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w

「SPOJ1487」Query on a tree III

「SPOJ1487」Query on a tree III 传送门 把树的 \(\text{dfs}\) 序抠出来,子树的节点的编号位于一段连续区间,然后直接上建主席树区间第 \(k\) 大即可. 参考代码: #include <algorithm> #include <cstdio> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x"

「CF741D」Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

传送门 Luogu 解题思路 考虑把22个字符状压下来,易知合法情况就是状态中之多有一个1,这个可以暴力一点判断23次. 然后后就是 dsu on the tree 了. 细节注意事项 咕咕咕 参考代码 #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <cctype> #i

LibreOJ #2016. 「SCOI2016」美味

二次联通门 : LibreOJ #2016. 「SCOI2016」美味 /* LibreOJ #2016. 「SCOI2016」美味 dalao们都在说这题如果没有加法balabala就可以用可持久化trie解决了 然而我连那个也不会啊QAQ 此题用主席树 从高位到低位贪心 能填1就填1,也就是查询一段区间有没有某个范围的数 (然而由乃dalao说可持久化线段树和可持久化trie是一个东西) */ #include <cstdio> #include <iostream> #inc

LibreOJ #2013. 「SCOI2016」幸运数字

二次联通门 : LibreOJ #2013. 「SCOI2016」幸运数字 /* LibreOJ #2013. 「SCOI2016」幸运数字 树链剖分 + 线段树 + 线性基合并 没什么可说的 对原树进行树链剖分 然后建线段树 每个区间维护一段线性基 每次暴力把一段插入另一段中 最后线性基求最大即可 注意线性基求最大时一定是倒着枚举的 */ #include <cstdio> #include <iostream> const int BUF = 12312334; char Bu

「一」创建一个带 ssh 服务的基础镜像(修订版)--使用「docker commit」创建

在介绍如何创建带 ssh 服务的基础镜像之前,我们想回顾一下之前介绍过的内容,其中提到有三种创建镜像的常用办法: 从文件系统导入 从现有容器使用「docker commit」提交 使用 dockerfile 文件 build 本章将主要介绍后面 2 种方法. 步骤如下: $ sudo docker run -ti ubuntu:14.04 /bin/bash #首先,使用我们最熟悉的 「-ti」参数来创建一个容器. [email protected]:/# sshd bash: sshd: co

Loj #3111. 「SDOI2019」染色

Loj #3111. 「SDOI2019」染色 题目描述 给定 \(2 \times n\) 的格点图.其中一些结点有着已知的颜色,其余的结点还没有被染色.一个合法的染色方案不允许相邻结点有相同的染色. 现在一共有 \(c\) 种不同的颜色,依次记为 \(1\) 到 \(c\).请问有多少对未染色结点的合法染色方案? 输入格式 第一行有两个整数 \(n\) 和 \(c\),分别描述了格点图的大小和总的颜色个数. 之后两行,每行有 \(n\) 个整数:如果是 \(0\) 则表示对应结点未被染色,否

「CQOI2015」选数

「CQOI2015」选数 题目描述 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究.然而他很快发现工作量太大了,于是向你寻求帮助.你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个.由于方案数较大,你只需要输出其除以1000000007的余数即可. 输入输出格式 输入格式: 输入一行,包含4个空格分开的正整数,

「SCOI2012」喵星球上的点名

「SCOI2012」喵星球上的点名 填一个很久以前用 \(\texttt{AC}\) 自动机没填上的坑. 关于本题,能够通过本题的算法很多,这里作者采用的是后缀数组+树状数组的做法. 首先有一个显然的结论:若 \(s_2\) 是 \(s_1\) 的子串,则 \(s_1\) 一定存在一个后缀与 \(s_2\) 的最长公共前缀为 \(|s_2|\). 我们将读入的姓.名.询问串连成一个整体,形成一个字符串 \(s\),且在每一个姓.名.询问串中插入一个不存在文本中的字符,且保证询问串后插入的比姓名串