[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并)

题面

给出一个n个节点m条边的森林,每个节点都有一个权值。有两种操作:

  1. Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
  2. 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]森林(可持久化线段树+并查集+启发式合并)的更多相关文章

  1. 【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集

    3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status ...

  2. BZOJ2733[HNOI2012]永无乡——线段树合并+并查集+启发式合并

    题目描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达 ...

  3. [WC2005]双面棋盘(线段树+并查集)

    线段树+并查集维护连通性. 好像 \(700ms\) 的时限把我的常数超级大的做法卡掉了, 必须要开 \(O_2\) 才行. 对于线段树的每一个结点都开左边的并查集,右边的并查集,然后合并. \(Co ...

  4. [BZOJ 4668]冷战(带边权并查集+启发式合并)

    [BZOJ 4668]冷战(并查集+启发式合并) 题面 一开始有n个点,动态加边,同时查询u,v最早什么时候联通.强制在线 分析 用并查集维护连通性,每个点x还要另外记录tim[x],表示x什么时间与 ...

  5. 2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集)

    2022.02.27 CF811E Vladik and Entertaining Flags(线段树+并查集) https://www.luogu.com.cn/problem/CF811E Ste ...

  6. BZOJ - 3123 森林 (可持久化线段树+启发式合并)

    题目链接 先把初始边建成一个森林,每棵树选一个根节点递归建可持久化线段树.当添加新边的时候,把结点数少的树暴力重构,以和它连边的那个点作为父节点继承线段树,并求出倍增数组.树的结点数可以用并查集来维护 ...

  7. BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)

    BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...

  8. BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)

    首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...

  9. bzoj 4504: K个串 可持久化线段树+堆

    题目: Description 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一 个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次). 兔子们想 ...

随机推荐

  1. 【NOIP2016提高A组集训第4场11.1】平衡的子集

    题目 夏令营有N个人,每个人的力气为M(i).请大家从这N个人中选出若干人,如果这些人可以分成两组且两组力气之和完全相等,则称为一个合法的选法,问有多少种合法的选法? 分析 如果暴力枚举每个人被分到哪 ...

  2. 【NOIP2016提高A组模拟8.14】总结

    第一题是几何题,没去想直接弃疗.... 第二题觉得很像背包,但是单挑人的顺序不同,答案也会不同,我比较了每个人先后的优劣性,成功搞定了这道题.但是再输出时不小心搞错了,爆零. 第三题,我答案了整整一个 ...

  3. tomcat7 与tomcat8 使用tomcat dbcp pool注意对应类变化

    tomcat dbcp pool在tomcat 7 和tomcat8下的jar包有变化,相应包名也发生变化,对应类名有相应变化! tomcat的lib文件夹下会有jar包tomcat-dbcp.jar ...

  4. spring boot2.0.2,<-1.4.8

    DataSourceBuilder cannot be resolved DataSourceBuilder cannot be resolved to a type RelaxedPropertyR ...

  5. ThoughtWorks.QRCode 生成二维码名片(实现二维码内容换行)

    最近在写一个很简单的功能,按照Vcard的格式,生成二维码名片.本来以为分分钟完事的事情,替换数据,直接调用dll去生成二维码. 测试时,发现生成的二维码使用微信扫描得到的名片信息为空,反向解析发现, ...

  6. 消息队列之--RocketMQ

    序言 资料 https://github.com/alibaba/RocketMQ http://rocketmq.apache.org/

  7. osm(Openstreetmap)数据下载并导入arcgis

    https://3nice.cc/2018/07/18/arcgisosm/ https://blog.csdn.net/zimojiang/article/details/80409139 http ...

  8. Flume的断点续传解决

    根据需求,首先定义以下3大要素 采集源,即source——监控文件内容更新 :  exec  ‘tail -F file’ 下沉目标,即sink——HDFS文件系统  :  hdfs sink Sou ...

  9. spring cloud:gateway-eureka

    gateway-server-eureka 1. File-->new spring starter project 2.add dependency <dependency> &l ...

  10. VSCode - 使用 WSL(Windows Subsystem for Linux)

    一开始我是只将 VSCode 集成的终端改成 WSL 的 Bash,结果发现内置的 GIt 用的还是 Windows 的 Git,Git Hooks 用的 Windows 的环境,上网搜了一下发现有很 ...