[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)
[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并)
题面
给出一个n个节点m条边的森林,每个节点都有一个权值。有两种操作:
- Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
- L x y在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。
分析
用并查集维护连通性以及每个联通块的大小
用主席树维护路径上第k大,第x棵主席树维护的是节点x到根的链上权值的出现情况,类似[BZOJ2588]Count on a tree(LCA+主席树)。不过这道题不用dfs序,直接根据编号建树。
合并x,y的时候启发式合并。若x子树大小比y小,就重新dfs一遍x的子树,并把y到根的链上权值加进去。反之同理。详情见代码。时间复杂度\(O(n \log ^2n)\)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define INF 0x3f3f3f3f
#define maxn 100000
#define maxlogn 22
#define maxnlogn 10000000
using namespace std;
inline void qread(int &x){
x=0;
int sign=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
x=x*sign;
}
inline void qprint(int x){
if(x<0){
putchar('-');
qprint(-x);
}else if(x==0){
putchar('0');
return;
}else{
if(x>=10) qprint(x/10);
putchar('0'+x%10);
}
}
int testcase;//是测试点编号,不是数据组数...
int n,m,q,mv;
int val[maxn+5];
int tmp[maxn+5];
int discrete(int *a,int n){
int sz=0;
for(int i=1;i<=n;i++) tmp[i]=a[i];
sort(tmp+1,tmp+1+n);
sz=unique(tmp+1,tmp+1+n)-tmp-1;
for(int i=1;i<=n;i++){
a[i]=lower_bound(tmp+1,tmp+1+sz,a[i])-tmp;
}
return sz;
}
struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int head[maxn+5];
int esz;
void add_edge(int u,int v){
esz++;
E[esz].from=u;
E[esz].to=v;
E[esz].next=head[u];
head[u]=esz;
esz++;
E[esz].from=v;
E[esz].to=u;
E[esz].next=head[v];
head[v]=esz;
}
struct disjoint_set{
int fa[maxn+5];
int sz[maxn+5];
void ini(int n){
for(int i=1;i<=n;i++){
fa[i]=i;
sz[i]=1;
}
}
int find(int x){
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
inline int size(int x){//返回x所在联通块大小
return sz[find(x)];
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(sz[fx]>sz[fy]) swap(fx,fy);
fa[fx]=fy;
sz[fy]+=sz[fx];
}
}S;
struct persist_segment_tree{
#define lson(x) (tree[x].ls)
#define rson(x) (tree[x].rs)
struct node{
int ls;
int rs;
int val;
}tree[maxnlogn+5];
int ptr;
inline void push_up(int x){
tree[x].val=tree[lson(x)].val+tree[rson(x)].val;
}
void update(int &x,int last,int upos,int l,int r){
x=++ptr;
tree[x]=tree[last];
if(l==r){
tree[x].val++;
return;
}
int mid=(l+r)>>1;
if(upos<=mid) update(lson(x),lson(last),upos,l,mid);
else update(rson(x),rson(last),upos,mid+1,r);
push_up(x);
}
int query(int x,int y,int lc,int lcfa,int k,int l,int r){
if(l==r){
return l;
}
int mid=(l+r)>>1;
int lcnt=tree[lson(x)].val+tree[lson(y)].val-tree[lson(lc)].val-tree[lson(lcfa)].val;
if(k<=lcnt) return query(lson(x),lson(y),lson(lc),lson(lcfa),k,l,mid);
else return query(rson(x),rson(y),rson(lc),rson(lcfa),k-lcnt,mid+1,r);
}
#undef lson
#undef rson
}T;
int root[maxn+5];
int log2n;
int deep[maxn+5];
int anc[maxn+5][maxlogn+5];
void dfs(int x,int fa){
deep[x]=deep[fa]+1;
anc[x][0]=fa;
for(int i=1;i<=log2n;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
T.update(root[x],root[fa],val[x],1,mv);
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa){
dfs(y,x);
}
}
}
int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
for(int i=log2n;i>=0;i--){
if(deep[anc[x][i]]>=deep[y]) x=anc[x][i];
}
if(x==y) return x;
for(int i=log2n;i>=0;i--){
if(anc[x][i]!=anc[y][i]){
x=anc[x][i];
y=anc[y][i];
}
}
return anc[x][0];
}
void merge(int x,int y){
if(S.size(x)>S.size(y)) swap(x,y);
S.merge(x,y);
add_edge(x,y);
if(deep[y]==0) deep[y]=1;
dfs(x,y);//注意不是dfs(x,0),因为x的父亲是y
}
int query(int x,int y,int k){
int lc=lca(x,y);
int id=T.query(root[x],root[y],root[lc],root[anc[lc][0]],k,1,mv);
return tmp[id];
}
int main(){
// freopen("5.in","r",stdin);
int last=0;
char op[2];
int x,y,k;
qread(testcase);
qread(n);
qread(m);
qread(q);
S.ini(n);
log2n=log2(n)+1;
for(int i=1;i<=n;i++) qread(val[i]);
mv=discrete(val,n);
for(int i=1;i<=m;i++){
qread(x);
qread(y);
add_edge(x,y);
S.merge(x,y);
}
for(int i=1;i<=n;i++){
if(!anc[i][0]) dfs(i,0);
}
for(int i=1;i<=q;i++){
scanf("%s",op);
if(op[0]=='Q'){
qread(x);
qread(y);
qread(k);
x^=last;
y^=last;
k^=last;
last=query(x,y,k);
qprint(last);
putchar('\n');
}else{
qread(x);
qread(y);
x^=last;
y^=last;
merge(x,y);
}
}
}
/*
1
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3
Q 3 5 1
Q 10 0 0
L 5 4
L 3 2
L 0 7
Q 9 2 5
Q 6 1 6
*/
[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)的更多相关文章
- 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集
3673: 可持久化并查集 by zky Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 1878 Solved: 846[Submit][Status ...
- BZOJ2733[HNOI2012]永无乡——线段树合并+并查集+启发式合并
题目描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达 ...
- [WC2005]双面棋盘(线段树+并查集)
线段树+并查集维护连通性. 好像 \(700ms\) 的时限把我的常数超级大的做法卡掉了, 必须要开 \(O_2\) 才行. 对于线段树的每一个结点都开左边的并查集,右边的并查集,然后合并. \(Co ...
- [BZOJ 4668]冷战(带边权并查集+启发式合并)
[BZOJ 4668]冷战(并查集+启发式合并) 题面 一开始有n个点,动态加边,同时查询u,v最早什么时候联通.强制在线 分析 用并查集维护连通性,每个点x还要另外记录tim[x],表示x什么时间与 ...
- 2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集)
2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集) https://www.luogu.com.cn/problem/CF811E Ste ...
- BZOJ - 3123 森林 (可持久化线段树+启发式合并)
题目链接 先把初始边建成一个森林,每棵树选一个根节点递归建可持久化线段树.当添加新边的时候,把结点数少的树暴力重构,以和它连边的那个点作为父节点继承线段树,并求出倍增数组.树的结点数可以用并查集来维护 ...
- BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)
BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...
- BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)
首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...
- bzoj 4504: K个串 可持久化线段树+堆
题目: Description 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一 个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次). 兔子们想 ...
随机推荐
- 获取树莓派ip地址的方法
1.有显示器 (1)鼠标停留在屏幕右上角网络图标上,2-3秒会显示网络连接信息(2) 图形界面下打开终端运行ifconfig命令 2.登录路由器查看名叫raspberry的设备 3.电脑上运行命 ...
- 为什么要用setTimeout模拟setInterval ?
setInterval有两个缺点: 使用setInterval时,某些间隔会被跳过: 可能多个定时器会连续执行: 在前一个定时器执行完前,不会向队列插入新的定时器(解决缺点一) 保证定时器间隔(解决缺 ...
- Linux 环境下 gzip 的加解密命令
1.加密 [root@127-0-0-1 nginx]# gzip -v access.log-20190328 access.log-20190328: 95.8% -- replaced with ...
- codevs 1057 津津的储蓄计划 2004年NOIP全国联赛提高组 x
时间限制: 1 s 空间限制: 128000 KB 题目描述 Description 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津300元钱,津津会预算这个月的花销,并且总能做到实际花 ...
- 【BZOJ1066】 [SCOI2007]蜥蜴
Description 在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外. 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平 ...
- 关于同时可用git命令clone和TortoiseGit拉取代码不需要密码
工作需要在windows7下使用git分布式版本控制系统,需要同时可以在git命令行模式或TortoiseGit拉取代码而不需要每次输入密码. 这时候需要同时安装git和TortoiseGit. gi ...
- scss 用法 及 es6 用法讲解
scss 用法的准备工作,下载 考拉 编译工具 且目录的名字一定不能出现中文,哪里都不能出现中文,否则就会报错 es6 用法 let 和 const let 声明变量的方式 在 {} 代码块里面才 ...
- HDU6025 Coprime Sequence(gcd)
HDU6025 Coprime Sequence 处理出数列的 \(gcd\) 前缀和后缀,删除一个数后的 \(gcd\) 为其前缀和后缀的 \(gcd\) . 遍历数列取 \(max\) 即为答案. ...
- C++获取寄存器eip的值
程序中需要打印当前代码段位置 如下 #include <stdio.h> #include <stdlib.h> #include <math.h> #ifdef ...
- 如果将get请求转换成post请求
td><a href="emp/${emp.id}">Edit</a></td> <form action="" ...