这题的想法真的很妙啊。

看到题的第一眼,我先想到树链剖分,并把\(DFS\)序当成一段区间上主席树。但是会发现在询问的时候,可能会非常复杂,因为你需要把路径拆成很多条轻链和重链,它们还不一定连续,很难做(这个做法貌似可以用于子树第\(k\)大问题)。

于是我们换一个思路,让某个点的从它的父亲继承信息,也就是让这个结点对应的主席树维护一条从它到根的链。

那查询该如何是好?假设要查询的结点为\(x,y\),我们利用树上差分的思想让\(x,y,lca(x,y),fa[lca(x,y)]\)对应的四棵主席树一起向下跳就行了。

有连边操作时,就要用到启发式合并了,也就是把小的那棵树合并到大的中,暴力重构一下小树的倍增数组和主席树就行了。

代码:

#include <bits/stdc++.h>

using namespace std;

#define N 100000
#define pb push_back
#define mp make_pair
#define mid ((l+r)>>1)
#define pii pair<int, int>
#define D 17 int n0, n, m, q, T, root[N+5], sz[N+5], d[N+5], me[N+5];
int w0[N+5], w[N+5], as[N+5], f[N+5][20];
int nid, sumv[100*N+5], lson[100*N+5], rson[100*N+5];
int lastans;
vector<int> G[N+5]; void build(int &o, int l, int r)
{
o = ++nid;
if(l == r) return ;
build(lson[o], l, mid), build(rson[o], mid+1, r);
} int LCA(int x, int y)
{
if(d[x] < d[y]) swap(x, y);
for(int i = D; i >= 0; --i)
if(d[f[x][i]] >= d[y]) x = f[x][i];
if(x == y) return x;
for(int i = D; i >= 0 && f[x][0] != f[y][0]; --i)
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
} void insert(int o, int &u, int l, int r, int x, int k)
{
u = ++nid;
lson[u] = lson[o], rson[u] = rson[o], sumv[u] = sumv[o]+k;
if(l == r) return ;
if(x <= mid) insert(lson[o], lson[u], l, mid, x, k);
else insert(rson[o], rson[u], mid+1, r, x, k);
} int query(int x, int y, int k) //x-->y 第k大
{
int lca = LCA(x, y), fa = f[lca][0], l = 1, r = n0;
x = root[x], y = root[y], lca = root[lca], fa = root[fa];
while(l != r)
{
int t = sumv[lson[x]]+sumv[lson[y]]-sumv[lson[fa]]-sumv[lson[lca]];
if(t >= k) r = mid, x = lson[x], y = lson[y], fa = lson[fa], lca = lson[lca];
else k -= t, l = mid+1, x = rson[x], y = rson[y], fa = rson[fa], lca = rson[lca];
}
return as[l];
} void dfs(int u, int fa, int anc)
{
d[u] = d[fa]+1, sz[u] = 1, f[u][0] = fa, me[u] = anc;
for(int i = 1; i <= D; ++i) f[u][i] = f[f[u][i-1]][i-1];
insert(root[fa], root[u], 1, n0, w[u], 1);
for(int i = 0, v; i < G[u].size(); ++i)
{
v = G[u][i];
if(v == fa) continue;
dfs(v, u, anc);
sz[u] += sz[v];
}
} void link(int x, int y)
{
G[x].pb(y), G[y].pb(x);
if(sz[me[x]] < sz[me[y]]) dfs(x, y, me[y]);
else dfs(y, x, me[x]);
} int main()
{
#ifndef ONLINE_JUDGE
freopen("testdata.in", "r", stdin);
freopen("testdata.out", "w", stdout);
#endif
scanf("%d", &T);
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; ++i) scanf("%d", &w0[i]), w[i] = w0[i]; //离散化
sort(w0+1, w0+n+1);
n0 = unique(w0+1, w0+n+1)-w0-1;
for(int i = 1, t; i <= n; ++i)
{
t = lower_bound(w0+1, w0+n0+1, w[i])-w0;
as[t] = w[i], w[i] = t;
}
for(int i = 1, x, y; i <= m; ++i)
{
scanf("%d%d", &x, &y);
G[x].pb(y), G[y].pb(x);
}
build(root[0], 1, n0);
for(int i = 1; i <= n; ++i)
if(!d[i]) dfs(i, 0, i);
char c;
for(int i = 1, x, y, z; i <= q; ++i)
{
cin >> c;
scanf("%d%d", &x, &y);
x ^= lastans, y ^= lastans;
if(c == 'Q') scanf("%d", &z), z ^= lastans, printf("%d\n", lastans = query(x, y, z));
else link(x, y);
}
return 0;
}

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

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

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

  2. [bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

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

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

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

  4. luoguP3302 [SDOI2013]森林 主席树 启发式合并

    题目链接 luoguP3302 [SDOI2013]森林 题解 本来这题树上主席树暴力启发式合并就完了 结果把lca写错了... 以后再也不这么写了 复杂度\(O(nlog^2n)\) "f ...

  5. [BZOJ3123][Sdoi2013]森林 主席树+启发式合并

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

  6. 【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

    我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时 ...

  7. 【BZOJ-3123】森林 主席树 + 启发式合并

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

  8. P3302 [SDOI2013]森林(主席树+启发式合并)

    P3302 [SDOI2013]森林 主席树+启发式合并 (我以前的主席树板子是错的.......坑了我老久TAT) 第k小问题显然是主席树. 我们对每个点维护一棵包含其子树所有节点的主席树 询问(x ...

  9. BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并

    BZOJ_3123_[Sdoi2013]森林_主席树+启发式合并 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20 ...

随机推荐

  1. Program Thread 和 Process的不同点

    Thread is for execution Kernel level thread, physical parallelism Cores Divide work amount of physic ...

  2. JavaScript是如何工作的:引擎,运行时和调用堆栈的概述!

    摘要: 理解JS执行原理. 原文:JavaScript是如何工作的:引擎,运行时和调用堆栈的概述! 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 本文是旨在深入研究JavaScrip ...

  3. hadoop集群的搭建

    hadoop集群的搭建 1.ubuntu 14.04更换成阿里云源 刚刚开始我选择了nat模式,所有可以连通网络,但是不能ping通,我就是想安装一下mysql,因为安装手动安装mysql太麻烦了,然 ...

  4. 在SuperMap iDesktop中如何快速追加记录行?

    SuperMap iDesktop 产品中,普通数据集右键查看属性表,无法编辑行.是因为要直接在属性表中添加行,只能是纯属性数据集才可用. 除了直接打开数据集,增加几何对象,还有什么办法可以快速追加记 ...

  5. 一种动态写入apk数据的方法(用于用户关系绑定、添加渠道号等)

    背景: 正在开发的APP需要记录业务员与客户的绑定关系.具体应用场景如下: 由流程图可知,并没有用户填写业务人员信息这一步,因此在用户下载的APP中就已经携带了业务人员的信息. 由于业务人员众多,不可 ...

  6. The xp_cmdshell proxy account information cannot be retrieved or is invalid. Verify that the '##xp_cmdshell_proxy_account##' credential exists and contains valid information.

    In one of our recent migrations, we got the following error when the client tried to fire xp_cmdshel ...

  7. 分布式系统CAP理论

    在单机的数据库系统之中,我们很容易实现一套满足ACID 特性的 事务处理系统, 事务的一致性不存在问题. 但是在分布式系统之中,由于数据分布在不同的主机结点上,如何对着些数据进行分布式的事务处理就具有 ...

  8. 自定义AXI总线形式SPI接口IP核,点亮OLED

    一.前言 最近花费很多精力在算法仿真和实现上,外设接口的调试略有生疏.本文以FPGA控制OLED中的SPI接口为例,重新夯实下基础.重点内容为SPI时序的RTL设计以及AXI-Lite总线分析.当然做 ...

  9. centos 7 进入图形界面

    1 确保自己已经安装图形界面,如果没有请执行命令 yum groupinstall "GNOME Desktop" "Graphical Administration T ...

  10. day22 面向对象

    面向对象 ''''1.面向过程编程   核心是"过程"二字,过程指的是解决问题的步骤,即先干什么再干什么   基于该思想编写程序就好比在编写一条流水线,是一种机械式的思维方式​   ...