Description

给你一片森林, 支持两个操作: 查询$x$到$y$的$K$大值,  连接两棵树中的两个点

Solution

对每个节点$x$动态开权值线段树, 表示从$x$到根节点路径上权值出现的次数。

查询时差分即可: $sum[x]+sum[y]-sum[lca]-sum[f[lca]]$

连边时需要启发式合并,将节点数小的接到节点数大的上去, 再遍历小的树, 并更新权值

我刚开始以为testcase是数据组数, TLE我好久,,

Code

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
using namespace std; const int N = 1e5; int lson[N * ], rson[N * ], sum[N * ], root[N];
int n, m, T, a[N], b[N], f[N][], head[N], tot, dep[N];
int father[N], num[N], cnt, nd_num;
int lastans, Case; struct edge {
int nxt, to;
}e[N << ]; int read() {
int X = , p = ; char c = getchar();
for(; c > '' || c < ''; c = getchar()) if(c == '-') p = -;
for(; c >= '' && c <= ''; c = getchar()) X = X * + c - '';
return X * p;
} void add(int u, int v) {
e[++tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
} int find_anc(int x) {
return father[x] == x ? x : father[x] = find_anc(father[x]);
} int find_lca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = ; ~i; --i) if(dep[f[x][i]] >= dep[y])
x = f[x][i];
if(x == y) return x;
for(int i = ; ~i; --i) if(f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][];
} int fd(int x) {
return lower_bound(b + , b + + cnt, x) - b;
} void ins(int &o, int now, int l, int r, int pos) {
o = ++nd_num;
sum[o] = sum[now] + ;
lson[o] = lson[now];
rson[o] = rson[now];
if(l == r) return;
int mid = (l + r) >> ;
if(pos <= mid) ins(lson[o], lson[now], l, mid, pos);
else ins(rson[o], rson[now], mid + , r, pos);
} int query(int x, int y, int lca, int flca, int l, int r, int k) {
if(l == r) return l;
int mid = (l + r) >> , tmp;
if((tmp = sum[lson[x]] + sum[lson[y]] - sum[lson[lca]] - sum[lson[flca]]) >= k) return query(lson[x], lson[y], lson[lca], lson[flca], l, mid, k);
else return query(rson[x], rson[y], rson[lca], rson[flca], mid + , r, k - tmp);
} void dfs(int u) {
dep[u] = dep[f[u][]] + ;
for(int i = ; i <= ; ++i)
f[u][i] = f[f[u][i - ]][i - ];
ins(root[u], root[f[u][]], , cnt, fd(a[u]));
for(int i = head[u]; i; i = e[i].nxt) {
int nt = e[i].to;
if(nt == f[u][]) continue;
f[nt][] = u;
dfs(nt);
}
} int work() {
lastans = ; tot = ;
nd_num = ;
/* memset(root, 0, sizeof(root));
memset(lson, 0, sizeof(lson));
memset(rson, 0, sizeof(rson));
memset(head, 0, sizeof(head));
memset(dep, 0, sizeof(dep));*/
n = rd; m = rd; T = rd;
for(int i = ; i <= n; ++i) b[i] = a[i] = rd;
sort(b + , b + + n);
cnt = unique(b + , b + + n) - b - ;
for(int i = ; i <= n; ++i) father[i] = i, num[i] = ;
for(int i = ; i <= m; ++i) {
int u = rd, v = rd;
int x = find_anc(u), y = find_anc(v);
father[y] = x;
num[x] += num[y];
add(u, v); add(v, u);
}
for(int i = ; i <= n; ++i) if(!dep[i]) dfs(i);
for(int i = ; i <= T; ++i) {
char c = getchar();
while(c != 'Q' && c != 'L') c = getchar();
int u = rd ^ lastans, v = rd ^ lastans;
if(c == 'Q') {
int lca = find_lca(u, v), flca = f[lca][], k = rd ^ lastans;
lastans = query(root[u], root[v], root[lca], root[flca], , cnt, k);
if(lastans > cnt || lastans < ) return printf("F**k,WA\n"), ;
lastans = b[lastans];
printf("%d\n", lastans);
}
else {
int x = find_anc(u), y = find_anc(v);
if(num[x] < num[y]) {
swap(x, y); swap(u, v);
}
father[y] = x;
num[x] += num[y];
f[v][] = u;
add(v, u); add(u, v);
dfs(v);
}
}
return ;
} int main()
{
Case = rd;
work();
}

BZOJ 3123 [SDOI2013] 森林 - 启发式合并 主席树的更多相关文章

  1. 【BZOJ3123】[SDOI2013] 森林(启发式合并主席树)

    点此看题面 大致题意: 给你一片森林,有两种操作:询问两点之间的第\(k\)小点权和在两棵树之间连一条边. 前置技能:树上主席树 做这道题目,我们首先要会树上主席树. 关于树上主席树,这有一道很好的例 ...

  2. bzoj 3674: 可持久化并查集加强版 (启发式合并+主席树)

    Description Description:自从zkysb出了可持久化并查集后……hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可以轻松虐!zky:…… ...

  3. BZOJ 2733 [HNOI2012]永无乡 - 启发式合并主席树

    Description 1: 查询一个集合内的K大值 2: 合并两个集合 Solution 启发式合并主席树板子 Code #include<cstdio> #include<cst ...

  4. BZOJ 3123: [Sdoi2013]森林 [主席树启发式合并]

    3123: [Sdoi2013]森林 题意:一个森林,加边,询问路径上k小值.保证任意时刻是森林 LCT没法搞,树上kth肯定要用树上主席树 加边?启发式合并就好了,小的树dfs重建一下 注意 测试点 ...

  5. Bzoj 3123: [Sdoi2013]森林(主席树+启发式合并)

    3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MB Description Input 第一行包含一个正整数testcase,表示当前 ...

  6. BZOJ3123: [Sdoi2013]森林(启发式合并&主席树)

    3123: [Sdoi2013]森林 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4813  Solved: 1420[Submit][Status ...

  7. bzoj 3123: [Sdoi2013]森林(45分暴力)

    3123: [Sdoi2013]森林 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4184  Solved: 1235[Submit][Status ...

  8. bzoj 3123 [Sdoi2013]森林(主席树,lca,启发式合并)

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

  9. bzoj 3123 [Sdoi2013]森林(主席树+启发式合并+LCA)

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

随机推荐

  1. 数组的es6新方法

    1.数组去重 var  changeReArr=(arr)=>{ return Array.from(new Set([1,2,2,3,5,4,5]))//利用set将[1,2,2,3,5,4, ...

  2. 关于python中的is和==的区别

    Python 中的比较:is 与 ==   在 Python 中会用到对象之间比较,可以用 ==,也可以用 is .但是它们的区别是什么呢? is 比较的是两个实例对象是不是完全相同,它们是不是同一个 ...

  3. R各种数据类型的转换

    1.列表转化为数据框 df <- data.frame(matrix(unlist(列表), nrow=132, byrow=T),stringsAsFactors=FALSE)

  4. linux下mysql升级

    最近漏洞扫描,扫描出了数据库存在中高危漏洞,于是迫切需要进行数据库升级.上网查了各种资料,说法很多,也到自己虚拟机上试了好多方法,终于倒腾出来,做下小总结记录一下. 升级操作: 1.到mysql官网h ...

  5. SO减单后MO分配给其他SO的问题修复

    逻辑:MO取进FP系统之前,首先判断是否带有SO号+SO行号,如果带有SO号+SO行号,则判断此SO号和SO行号是否存在订单表中,如果不存在则表示此MO对应的订单已减单,此MO需要做过滤,并展现在报表 ...

  6. Android 支付宝/微信支付结果判断

    微信支付结果码 private static final int PAY_OK = 0; //交易成功 private static final int PAY_ERR = -1; //交易失败 pr ...

  7. 【转】自动化测试框架: pytest&allure ,提高自动化健壮性和稳定性

    序 在之前,我写过一个系列“从零开始搭建一个简单的ui自动化测试框架(pytest+selenium+allure)”,在这个系列里,主要介绍了如何从零开始去搭建一个可用的自动化工程框架,但是还缺乏了 ...

  8. jackson java对象和json对象的互相转换

    概述 Jackson框架是基于Java平台的一套数据处理工具,被称为“最好的Java Json解析器”. Jackson框架包含了3个核心库:streaming,databind,annotation ...

  9. 模板】AC自动机(简单版)

    模板]AC自动机(简单版) https://www.luogu.org/problemnew/show/P3808 这是一道简单的AC自动机模板题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保 ...

  10. Wannafly挑战赛13 C:zzf的好矩阵(思维)

    题目描述 一个8 * 8的棋盘,第一个格子放1个麦穗,第二个格子放2个麦穗,第三个格子放4个麦穗……那么最后,共要放几个麦穗呢? zzf表示这个问题实在太简单,于是重新规定了游戏的规则. 初始的棋盘为 ...