bzoj3123 [Sdoi2013]森林 树上主席树+启发式合并
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=3123
题解
如果是静态的查询操作,那么就是直接树上主席树的板子。
但是我们现在有了一个连接两棵树的操作。
那么我们就采取启发式合并的方法来暴力重新建主席树。
对于表示树上前缀和的主席树来说,连接两条边以后,会影响到的点只有其中的一棵树。
所以不妨把较小的那一棵树暴力重新建立主席树。
另外由于树形态不固定,所以要用倍增 lca。
时间复杂度 \(O(n\log^2n)\),空间复杂度 \(O(q\log^n)\)。(也许垃圾节点回收可以做做到一个 \(log\) ?)
输入的第一个数据不是数据组数!!!被坑了好久。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 8e4 + 7;
const int LOG = 18;
int n, m, Q, nod, dis, la;
int a[N], b[N], f[N][LOG], dep[N], siz[N], fa[N], rt[N];
struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }
inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
struct Node { int lc, rc, val; } t[N * 16 * 16];
inline void ins(int &o, int p, int L, int R, int x) {
// dbg("o = %d, p = %d, L = %d, R = %d, x = %d\n", o, p, L, R, x);
t[o = ++nod] = t[p], ++t[o].val;
if (L == R) return;
int M = (L + R) >> 1;
if (x <= M) ins(t[o].lc, t[p].lc, L, M, x);
else ins(t[o].rc, t[p].rc, M + 1, R, x);
}
inline int qval(int o1, int o2, int p1, int p2, int L, int R, int k) {
// dbg("o1 = %d, o2 = %d, p1 = %d, p2 = %d, L = %d, R = %d, k = %d, t[o1].val = %d, t[o2].val = %d, t[p1].val = %d, t[p2].val = %d\n", o1, o2, p1, p2, L, R, k, t[o1].val, t[o2].val, t[p1].val, t[p2].val);
if (L == R) return L;
int M = (L + R) >> 1, ls = t[t[o1].lc].val + t[t[o2].lc].val - t[t[p1].lc].val - t[t[p2].lc].val;
if (k <= ls) return qval(t[o1].lc, t[o2].lc, t[p1].lc, t[p2].lc, L, M, k);
else return qval(t[o1].rc, t[o2].rc, t[p1].rc, t[p2].rc, M + 1, R, k - ls);
}
inline void dfs(int x, int rt, int fa = 0) {
// dbg("x = %d, rt = %d, fa = %d\n", x, rt, fa);
dep[x] = dep[fa] + 1, f[x][0] = fa, siz[x] = 1;
ins(::rt[x], ::rt[fa], 1, dis, a[x]);
if (rt) ::fa[x] = rt;
for (int i = 1; i < LOG; ++i) f[x][i] = f[f[x][i - 1]][i - 1];
for fec(i, x, y) if (y != fa) dfs(y, rt, x), siz[x] += siz[y];
}
inline int lca(int x, int y) {
if (dep[x] < dep[y]) std::swap(x, y);
for (int i = LOG - 1; ~i; --i) if (dep[f[x][i]] >= dep[y]) x = f[x][i];
if (x == y) return x;
for (int i = LOG - 1; ~i; --i) if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
inline void lsh() {
std::sort(b + 1, b + n + 1);
dis = std::unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i) a[i] = std::lower_bound(b + 1, b + dis + 1, a[i]) - b;
}
inline void work() {
lsh();
for (int i = 1; i <= n; ++i) if (!dep[i]) dfs(i, i);
// dbg("***********\n");
while (Q--) {
char opt[5];
scanf("%s", opt);
if (*opt == 'Q') {
int x, y, k, p;
read(x), read(y), read(k);
x ^= la, y ^= la, k ^= la;
p = lca(x, y);
// dbg("x = %d, y = %d, k = %d, p = %d, la = %d\n", x, y, k, p, la);
printf("%d\n", la = b[qval(rt[x], rt[y], rt[p], rt[f[p][0]], 1, dis, k)]);
} else {
int x, y, fx, fy;
read(x), read(y);
x ^= la, y ^= la;
fx = find(x), fy = find(y);
// dbg("siz[fx] = %d, siz[fy] = %d\n", siz[fx], siz[fy]);
if (siz[fx] > siz[fy]) {
fa[fy] = fx, siz[fx] += siz[fy];
dfs(y, 0, x), adde(x, y);
} else {
fa[fx] = fy, siz[fy] += siz[fx];
dfs(x, 0, y), adde(x, y);
}
}
}
}
inline void init() {
read(n), read(m), read(Q);
for (int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i];
int x, y;
for (int i = 1; i <= m; ++i) read(x), read(y), adde(x, y);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
read(n);
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
bzoj3123 [Sdoi2013]森林 树上主席树+启发式合并的更多相关文章
- BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并
BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20 ...
- P3302 [SDOI2013]森林(主席树+启发式合并)
P3302 [SDOI2013]森林 主席树+启发式合并 (我以前的主席树板子是错的.......坑了我老久TAT) 第k小问题显然是主席树. 我们对每个点维护一棵包含其子树所有节点的主席树 询问(x ...
- BZOJ2123 [Sdoi2013]森林 【主席树 + 启发式合并】
题目 输入格式 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...
- [bzoj3123][sdoi2013森林] (树上主席树+lca+并查集启发式合并+暴力重构森林)
Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...
- bzoj 3123 [Sdoi2013]森林(主席树+启发式合并+LCA)
Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...
- [luoguP3302] [SDOI2013]森林(主席树 + 启发式合并 + lca)
传送门 显然树上第k大直接主席树 如果连边的话,我们重构小的那一棵,连到另一棵上. 说起来简单,调了我一晚上. 总的来说3个错误: 1.离散化写错位置写到了后面 2."="写成了& ...
- p3302 [SDOI2013]森林(树上主席树+启发式合并)
对着题目yy了一天加上看了一中午题解,终于搞明白了我太弱了 连边就是合并线段树,把小的集合合并到大的上,可以保证规模至少增加一半,复杂度可以是\(O(logn)\) 合并的时候暴力dfs修改倍增数组和 ...
- [bzoj3123][洛谷P3302] [SDOI2013]森林(树上主席树+启发式合并)
传送门 突然发现好像没有那么难……https://blog.csdn.net/stone41123/article/details/78167288 首先有两个操作,一个查询,一个连接 查询的话,直接 ...
- [bzoj3123][Sdoi2013]森林_主席树_启发式合并
森林 bzoj-3123 Sdoi-2013 题目大意:给定一片共n个点的森林,T个操作,支持:连接两个不在一棵树上的两个点:查询一棵树上路径k小值. 注释:$1\le n,T \le 8\cdot ...
随机推荐
- css简单实现带箭头的边框
原文地址 https://tianshengjie.cn/artic... css简单实现带箭头的边框 普通边框 <style> .border { width: 100px; heigh ...
- 修改mac默认python版本 为python3
mac一般自带python2.7 可以修改 ~/.bash_profile (具体的path取决于你的python3安装路径): vi ~/.bash_profile # 添加这一行 alias py ...
- day50—JavaScript鼠标拖拽事件
转行学开发,代码100天——2018-05-05 今天通过鼠标拖拽事件复习巩固一下鼠标事件. 鼠标拖拽事件需要记住两点: 1.距离不变 2.鼠标事件(按下,移动,抬起) <div id=&quo ...
- 测开之路八十六:python操作sqlite
创建sqlite数据库,并创建表和数据 python自带sqlite3库可以创建数据库文件 导入库:import sqlite3 创建游标,指定数据库名字:con = sqlite3.connect( ...
- 字符串中的TOUPPER函数
std::string& str_toupper(std::string& s) { std::transform(s.begin(), s.end(), s.begin(), []( ...
- MySQL-mysql 8.0.11安装教程 windows
网上的教程有很多,基本上大同小异.但是安装软件有时就可能因为一个细节安装失败.我也是综合了很多个教程才安装好的,所以本教程可能也不是普遍适合的. 安装环境:win7 1.下载zip安装包: MySQL ...
- linux下vscode备忘
vscode如何自定义,如何方便地编写c/c++vscode支持vim.sublime快捷键,在设置->keymap可以安装相应插件vscode默认的快捷键支持自定义,打开keyboard sh ...
- 【ABAP系列】SAP 业务界面同时显示KEY和文本
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP 业务界面同时显示KEY和 ...
- linux安装 redis(redis-3.0.2.tar.gz) 和 mongodb(mongodb-linux-x86_64-rhel62-4.0.0)
1:首先 要下载 这两个 压缩包 注意:liunx是否已经安装过 gcc没安装的话 先安装:yum install gcc-c++ 2:安装 redis:redis-3.0.2.tar.gz (1): ...
- yield,sleep,wait
转自:http://dylanxu.iteye.com/blog/1322066 1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也 ...