【BZOJ3123】[SDOI2013] 森林(启发式合并主席树)
大致题意: 给你一片森林,有两种操作:询问两点之间的第\(k\)小点权和在两棵树之间连一条边。
前置技能:树上主席树
做这道题目,我们首先要会树上主席树。
关于树上主席树,这有一道很好的例题:【洛谷2633】Count on a tree(只包含此题的询问操作)。
接下来,我们就将询问操作略过不提了(毕竟在这道题目中已经介绍过了),而主要讲讲如何连边。
启发式合并主席树
对于连边操作,我们需要启发式合并主席树(当然,也有一些奆佬是用\(LCT\)的)。
关于启发式合并的时间复杂度证明,我写过一篇很烂的博客,可以去看一下(其实它与按秩合并是同一个东西)。
如果要将两棵主席树合并,我们一般选择将深度小的主席树合并到深度大的主席树上。
千万注意,比较的是深度而不是子树大小!不然会像我一样\(T\)飞。
至于为什么比较深度,在此摘录\(hl666\)神犇的原话如下:
\(Excerpt\)
影响主席树时间效率的是深度,而不是子树大小,子树大小对时间效率是没有任何直接影响的。
哦,对了,还没有讲怎么合并。
其实也非常简单,直接对深度较小的主席树重新扫一遍进行更新即可(具体操作见代码吧)。
代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 1e9
#define Inc(x,y) ((x+=(y))>=MOD&&(x-=MOD))
#define ten(x) (((x)<<3)+((x)<<1))
#define N 80000
#define LogN 20
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,cnt,ee=0,a[N+5],p[N+5],lnk[N+5],Depth[N+5],fa[N+5][LogN+5];
struct edge
{
int to,nxt;
}e[(N<<1)+5];
class FIO
{
private:
#define Fsize 100000
#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
int f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
public:
FIO() {FinNow=FinEnd=Fin;}
inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=ten(x)+(ch&15),isdigit(ch=tc()));x*=f;}
inline void read_char(char &x) {while(isspace(x=tc()));}
inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
inline void write(int x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
inline void write_char(char x) {pc(x);}
inline void write_string(string x) {register int i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
inline int LCA(int x,int y)//倍增LCA
{
register int i;
if(Depth[x]<Depth[y]) swap(x,y);
for(i=0;Depth[x]^Depth[y];++i) if((Depth[x]^Depth[y])&(1<<i)) x=fa[x][i];
if(!(x^y)) return x;
for(i=0;fa[x][i]^fa[y][i];++i);
for(--i;i>=0;--i) if(fa[x][i]^fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
inline int GetRt(int x)//求出x所在树的根
{
register int i;
for(i=0;Depth[x];++i) if(Depth[x]&(1<<i)) x=fa[x][i];
return x;
}
class Class_ChairmanTree//主席树
{
private:
int n,tot,Root[N+5];
struct Tree
{
int Val,Size,Level,Son[2];
}node[N*LogN<<4];
inline void Build(int l,int r,int &rt)//建树
{
if(!rt&&(rt=++tot),!(l^r)) return;
register int mid=l+r>>1;
Build(l,mid,node[rt].Son[0]),Build(mid+1,r,node[rt].Son[1]);
}
inline void ins(int l,int r,int &rt,int lst,int val)//插入
{
if(node[rt=++tot]=node[lst],++node[rt].Size,!(l^r)) return;
register int mid=l+r>>1;
val<=mid?ins(l,mid,node[rt].Son[0],node[lst].Son[0],val):ins(mid+1,r,node[rt].Son[1],node[lst].Son[1],val);
}
inline int qry(int l,int r,int rt1,int rt2,int rt3,int rt4,int k)//询问
{
if(!(l^r)) return l;
register int mid=l+r>>1,t=node[node[rt3].Son[0]].Size+node[node[rt4].Son[0]].Size-node[node[rt1].Son[0]].Size-node[node[rt2].Son[0]].Size;
if(t>=k) return qry(l,mid,node[rt1].Son[0],node[rt2].Son[0],node[rt3].Son[0],node[rt4].Son[0],k);
else return qry(mid+1,r,node[rt1].Son[1],node[rt2].Son[1],node[rt3].Son[1],node[rt4].Son[1],k-t);
}
public:
inline void Init(int len) {Build(1,n=len,Root[0]);}//初始化
inline void Insert(int v,int nv,int val) {ins(1,n,Root[nv],Root[v],val);}
inline int Query(int v1,int v2,int k) {return qry(1,n,Root[LCA(v1,v2)],Root[fa[LCA(v1,v2)][0]],Root[v1],Root[v2],k);}
inline bool Smaller(int rt1,int rt2) {return node[rt1].Level<node[rt2].Level;}//判断rt1所在树的深度是否小于rt2所在树的深度
inline void AddLevel(int rt) {++node[rt].Level;}//将rt所在树的深度加1
}ChairmanTree;
inline int find(int x)//离散化
{
register int l=1,r=cnt,mid=l+r>>1;
for(;l<=r;mid=l+r>>1) p[mid]<x?l=mid+1:r=mid-1;
return l;
}
inline void Init(int x)//初始化
{
register int i;
for(ChairmanTree.Insert(fa[x][0],x,find(a[x])),i=1;i<=LogN;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
for(i=lnk[x];i;i=e[i].nxt) if(fa[x][0]^e[i].to) Depth[e[i].to]=Depth[x]+1,fa[e[i].to][0]=x,Init(e[i].to);
}
int main()
{
register int i,x,y,z,fx,fy,Q,ans=0;register char op;
for(F.read(n),F.read(n),F.read(m),F.read(Q),i=1;i<=n;++i) F.read(a[i]),p[i]=a[i];
for(i=1;i<=m;++i) F.read(x),F.read(y),add(x,y),add(y,x);
for(sort(p+1,p+n+1),ChairmanTree.Init(cnt=unique(p+1,p+n+1)-p-1),i=1;i<=n;++i) if(!fa[i][0]) Init(i);
while(Q--)
{
F.read_char(op),F.read(x),F.read(y);
if(op^'L') F.read(z),F.write(ans=p[ChairmanTree.Query(x^ans,y^ans,z^ans)]),F.write_char('\n');
else
{
fx=GetRt(x^=ans),fy=GetRt(y^=ans),add(x,y),add(y,x);//记得连边
if(ChairmanTree.Smaller(fx,fy)) swap(fx,fy),swap(x,y);//比较深度,选择深度较小的树合并到深度较大的树上
ChairmanTree.AddLevel(fx),fa[y][0]=x,Depth[y]=Depth[x]+1,Init(y);//重新建树
}
}
return F.end(),0;
}
【BZOJ3123】[SDOI2013] 森林(启发式合并主席树)的更多相关文章
- BZOJ 3123 [SDOI2013] 森林 - 启发式合并 主席树
Description 给你一片森林, 支持两个操作: 查询$x$到$y$的$K$大值, 连接两棵树中的两个点 Solution 对每个节点$x$动态开权值线段树, 表示从$x$到根节点路径上权值出 ...
- bzoj 3674: 可持久化并查集加强版 (启发式合并+主席树)
Description Description:自从zkysb出了可持久化并查集后……hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可以轻松虐!zky:…… ...
- BZOJ 2733 [HNOI2012]永无乡 - 启发式合并主席树
Description 1: 查询一个集合内的K大值 2: 合并两个集合 Solution 启发式合并主席树板子 Code #include<cstdio> #include<cst ...
- BZOJ3123: [Sdoi2013]森林(启发式合并&主席树)
3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4813 Solved: 1420[Submit][Status ...
- [bzoj3123][洛谷P3302] [SDOI2013]森林(树上主席树+启发式合并)
传送门 突然发现好像没有那么难……https://blog.csdn.net/stone41123/article/details/78167288 首先有两个操作,一个查询,一个连接 查询的话,直接 ...
- 【bzoj3123】[Sdoi2013]森林 倍增LCA+主席树+启发式合并
题目描述 输入 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...
- [Sdoi2013]森林(启发式合并+主席树)
对于操作1,显然可以使用主席树维护,然后对于一条链(x,y),假设lca为f,根为rt,则(rt,x)+(rt,y)-(rt,f)-(rt,fa[f])即为所求的链,在主席树上直接查询即可,查询方式类 ...
- p3302 [SDOI2013]森林(树上主席树+启发式合并)
对着题目yy了一天加上看了一中午题解,终于搞明白了我太弱了 连边就是合并线段树,把小的集合合并到大的上,可以保证规模至少增加一半,复杂度可以是\(O(logn)\) 合并的时候暴力dfs修改倍增数组和 ...
- 洛谷 P3302 [SDOI2013]森林 Lebal:主席树 + 启发式合并 + LCA
题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权 ...
随机推荐
- Codeforces#514D(三分,简单二维几何)
#include<bits/stdc++.h>using namespace std;const double eps=1e-8;int n; struct node{ double ...
- 51nod1024(math+set)
题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1024 题意:中文题诶- 思路:要是能求出a^b的值来就好了. ...
- 洛谷P3380 【模板】二逼平衡树(树套树)(线段树+树状数组)
P3380 [模板]二逼平衡树(树套树) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上的数 ...
- knockout+MVC+webapi+sqlserver完成增删查改
快过年了,公司的事情较少,想着开始学习点新东西.这段时间一个项目用到了mvc和webapi,然后一直对knockout比较感兴趣,就想着用这个框架做一个小实例.数据库采用的是sqlserver.话不多 ...
- spring cloud微服务三:Eureka服务治理之注册服务提供者及服务的发现和消费
当服务注册中心成功建立以后,就需要有服务的注册和消费,否则这个服务注册中心就没有了存在的意义,而实际上,一个简单的服务注册也是非常简单的,仅仅需要实现四部曲就好. 首先,还是建立一个基本的spring ...
- Exadata中Infiniband交换机升级
Infiniband交换机的软件补丁包,随着软件版本的不同,其发布方式也有所变化,从如下图表可以看出,1.3.3-2这个版本是一个分水岭,这个版本及以前的版本,补丁包是单独下载的,而之后的版本,inf ...
- .net core webapi框架
折腾了一段时间..还是用ef比较熟,哈哈 参考地址:https://www.cnblogs.com/danvic712/p/10331976.html --jwt 参考地址:https://www.c ...
- Angular学习笔记 ——input 标签上的【name属性】和【ngModelOptions属性】
利用“@angular/forms" 创建<form>表单的时候,系统默认会创建一个”FormGroup"的对象. 使用带有“ngModel"的”<in ...
- Luogu P2480 [SDOI2010]古代猪文 卢卡斯+组合+CRT
好吧刚开始以为扩展卢卡斯然后就往上套..结果奇奇怪怪又WA又T...后来才意识到它的因子都是质数...qwq怕不是这就是学知识学傻了.. 题意:$ G^{\Sigma_{d|n} \space C_n ...
- ACM-ICPC 2018 南京赛区网络预赛 B. The writing on the wall (暴力)
题意:一个n*m的方格矩阵,有的格子被涂成了黑色,问该矩阵中有多少个子矩阵,子矩阵不包含黑色格子; 思路:对于一个长为L, 高为H的无黑点矩阵中包含的高为H的子矩阵个数为L+(L-1)+(L-2)+. ...