[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组模拟9.17总结

    第一题,典型的隔板问题, 但是我忘记隔板问题怎么打,一开始在花了1小时,还是没想出来,果断弃疗, 最后的40分钟,我打完了第二题,接着又用了20分钟推敲出一种极其猥琐的式子来代替,可惜预处理的阶乘忘记 ...

  2. layui.form is not a function

    改变: var form = layui.form; 如果你是从1.x升级2.x造成的,那只需要在定义的时候吧后面的()去掉就可以了.请关注更新日志:1.x 升 2.0 特别注意事项layDate日期 ...

  3. webapi 参数的请求和接收

    数据传递和接收1.数据头为User-Agent: FiddlerAccept: application/xml; charset=utf-8Host: localhost:1258Content-Le ...

  4. PHP基础教程 php 网络上关于设计模式一些总结

    1.单例模式 单例模式顾名思义,就是只有一个实例.作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的要点有三个: 一是某个类只能有一个实例; 二 ...

  5. JPA学习(四、JPA_映射关联关系)

    框架学习之JPA(四) JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中 ...

  6. MongoDB基本操作(增删改查)

    基本操作      基本的“增删查改“,在DOS环境下输入mongo命令打开shell,其实这个shell就是mongodb的客户端,同时也是一个js的编译器,默认连接的是“test”数据库.  

  7. ASP.NET MVC 生成EML文件

    需求: 点发送邮件按钮的时候, 自动在客户端电脑打开默认邮件的窗口,并且把内容和附件都附加上去. 解决方案: 尝试使用过Microsoft.Office.Interop.Outlook 和 MPAI. ...

  8. 线程工具类ThreadUtils

    package yqw.java.util; public class ThreadUtils { /**     * showThreadInfo     *      * @return     ...

  9. tp32-layuicms项目介绍

    项目结构:  项目截图: 登录页 文章列表 码云仓库:https://gitee.com/lim2018/tp32-layuicms

  10. 白鹭http请求post

    示例demo: //new http请求 var request = new egret.HttpRequest(); //请求参数 var params = "p1=postP1& ...