[BZOJ3123][Sdoi2013]森林(主席树+启发式合并)

news/2024/12/27 11:42:36/

首先介绍下树上主席树:
在序列上的主席树,是对序列的每一个前缀都建立一个线段树版本。
而树上主席树,是对树上的每一个节点到根的路径都建立一个线段树版本。
而如果 query(u) 是查询节点 u 到根的路径上的信息,则查询u v 的路径上的信息,就是:
query(u)+query(v)query(lca(u,v))query(fa[lca(u,v)])
这样,就可以在权值线段树上二分,查询路径第 k 小值了。
而最后的问题就是加边。可以考虑启发式合并,也就是把节点数少的树合并到节点数多的树上面去,这样每个节点被合并的次数不超过O(logn)
在合并之后,也必须初始化节点数少的树的倍增LCA数组,以及节点数少的树的线段树版本。所以,一次合并的操作为 O(log2n)
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {int res = 0; bool bo = 0; char c;while (((c = getchar()) < '0' || c > '9') && c != '-');if (c == '-') bo = 1; else res = c - 48;while ((c = getchar()) >= '0' && c <= '9')res = (res << 3) + (res << 1) + (c - 48);return bo ? ~res + 1 : res;
}
inline char get() {char c; while ((c = getchar()) != 'L' && c != 'Q');return c;
}
const int N = 7e5 + 5, Orz = 2e7 + 5, LogN = 20;
struct cyx {int lc, rc, cnt;
} T[Orz];
int n, m, QAQ, rt[N], sze[N], ecnt, nxt[N], adj[N], go[N], fa[N][LogN],
val[N], RT[N], dep[N], lst;
bool vis[N];
void add_edge(int u, int v) {nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
void build(int y, int &x, int l, int r, int p) {T[x = ++QAQ] = T[y]; T[x].cnt++;int mid = l + r >> 1; if (l == r) return;if (p <= mid) build(T[y].lc, T[x].lc, l, mid, p);else build(T[y].rc, T[x].rc, mid + 1, r, p);
}
void dfs(int u, int fu, int r) {int i; build(rt[fu], rt[u], 0, 1e9, val[u]); sze[u] = 1; RT[u] = r;dep[u] = dep[fa[u][0] = fu] + 1;for (i = 0; i <= 17; i++) fa[u][i + 1] = fa[fa[u][i]][i];for (int e = adj[u], v; e; e = nxt[e]) {if ((v = go[e]) == fu) continue;dfs(v, u, r); sze[u] += sze[v];}vis[u] = 1;
}
void link(int x, int y) {if (sze[RT[x]] < sze[RT[y]]) swap(x, y);sze[RT[x]] += sze[RT[y]]; dfs(y, x, RT[x]);add_edge(x, y);
}
int lca(int u, int v) {int i; if (dep[u] < dep[v]) swap(u, v);for (i = 18; i >= 0; i--) {if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];if (u == v) return u;}for (i = 18; i >= 0; i--)if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];return fa[u][0];
}
int query(int p1, int p2, int p3, int p4, int l, int r, int k) {if (l == r) return l; int mid = l + r >> 1;int delta = T[T[p1].lc].cnt + T[T[p2].lc].cnt- T[T[p3].lc].cnt - T[T[p4].lc].cnt;if (delta >= k)return query(T[p1].lc, T[p2].lc, T[p3].lc, T[p4].lc, l, mid, k);elsereturn query(T[p1].rc, T[p2].rc, T[p3].rc, T[p4].rc, mid + 1, r, k - delta);
}
int ask(int x, int y, int k) {int z = lca(x, y);return query(rt[x], rt[y], rt[z], rt[fa[z][0]], 0, 1e9, k);
}
int main() {int i, x, y, k; read(); n = read(); m = read(); int Q = read();for (i = 1; i <= n; i++) val[i] = read();for (i = 1; i <= m; i++) x = read(), y = read(), add_edge(x, y);for (i = 1; i <= n; i++) if (!vis[i]) dfs(i, 0, i);while (Q--) {char op = get(); x = read() ^ lst; y = read() ^ lst;if (op == 'L') link(x, y);else k = read() ^ lst, printf("%d\n", lst = ask(x, y, k));}return 0;
}

http://www.ppmy.cn/news/846265.html

相关文章

zoj 3123 尺取法

这个也是训练赛上的一个题&#xff0c;当时一看1e9就没有直接爆&#xff0c;记得做过这个题&#xff0c;的确是白书的上一个原题&#xff0c; 这个题需要注意的就是题目的理解&#xff0c;暴力的时候&#xff0c;左右一个标尺&#xff0c;先从右开始&#xff0c;然后逐渐减左标…

bzoj 3123[Sdoi2013]森林 - 树上主席树 + 启发式合并

3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MB Description Input 第一行包含一个正整数testcase&#xff0c;表示当前测试数据的测试点编号。保证1≤testcase≤20。 第二行包含三个整数N&#xff0c;M&#xff0c;T&#xff0c;分别表示节点数、初始边数、操…

ZOJ - 3123 Subsequence (滑动窗口)

题意&#xff1a;给定N个数&#xff0c;求和大于等于S的最短连续子序列的长度。 分析&#xff1a;滑动窗口即可。两种写法。 1、 #include<cstdio> #include<cstring> #include<cstdlib> #include<cctype> #include<cmath> #include<iostream…

杭电3123

这是一道数论题目&#xff01;利用公式同余取模&#xff01;思路&#xff1a;n可以达到10^100&#xff0c;很明显不能直接处理。但是发现只要n>m&#xff0c;那么m!(m1)!...n!这些项都是可以被m整除的&#xff0c;要对m求余&#xff0c;只需要找比m小的阶乘即可&#xff0c;…

[bzoj3123][SDOI2013]森林

3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 1654 Solved: 520 [Submit][Status][Discuss] Description Input 第一行包含一个正整数testcase&#xff0c;表示当前测试数据的测试点编号。保证1≤testcase≤20。 第二行包含三个整数N&#xff…

bzoj 3123 [Sdoi2013]森林

http://www.elijahqi.win/archives/3755 Description Input 第一行包含一个正整数testcase&#xff0c;表示当前测试数据的测试点编号。保证1≤testcase≤20。 第二行包含三个整数N&#xff0c;M&#xff0c;T&#xff0c;分别表示节点数、初始边数、操作数。第三行包含N个…

hdu_3123_GCC

The GNU Compiler Collection (usually shortened to GCC) is a compiler system produced by the GNU Project supporting various programming languages. But it doesn’t contains the math operator “!”. In mathematics the symbol represents the factorial operation…

【loj3123】【CTS2019】重复

题目 给出一个长度为\(n\)的串\(s\)&#xff0c;询问有多少个长度为\(m\)的串\(t\)  满足 \(t\) 的无限循环串存在一个长度为\(n\)且比\(s\)字典序严格小的子串 $ n , m \le 2000 $ 题解 自从CTS打铁之后就非常自闭&#xff0c;智商一天不如一天&#xff0c;所以感觉这题异常抽…