题链:

http://www.lydsy.com/JudgeOnline/problem.php?id=3123

题解:

主席树,在线,启发式合并
简单版(只有询问操作):[2588: Spoj 10628. Count on a tree] [题解]

多了一个合并联通块的操作,所以采用启发式合并。
名字看似高大上,其实就是把小的那个联通块暴力连在大的上面。
(别想多了,暴力就是暴力,一一重新遍历小的联通块的点,然后重新对这些点建立主席树)

主席树启发式合并的总的复杂度:$Nlog_2^2N$
应该是每次合并的两个东西大小都相同时,复杂度最高(但不会证明,现在只能先记着),
在每个点需要花费的代价为 1 时,复杂度为 $Nlog_2N$,
但是主席树的每个点需要花费的代价为 $log_2N$,所以总的复杂度为 $Nlog_2^2N$

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 80500
using namespace std;
int A[MAXN],tmp[MAXN];
int bel[MAXN],siz[MAXN],dep[MAXN],fa[MAXN][20];
int N,M,T,tnt;
struct Edge{
int to[MAXN*2],nxt[MAXN*2],head[MAXN],ent;
Edge(){
ent=2; memset(head,0,sizeof(head));
}
void Adde(int u,int v){
to[ent]=v; nxt[ent]=head[u]; head[u]=ent++;
to[ent]=u; nxt[ent]=head[v]; head[v]=ent++;
}
}E;
struct CMT{
int rt[MAXN],ls[MAXN*400],rs[MAXN*400],cnt[MAXN*400],sz;
void Insert(int v,int &u,int l,int r,int p){
u=++sz; ls[u]=ls[v];
rs[u]=rs[v]; cnt[u]=cnt[v];
cnt[u]++; if(l==r) return;
int mid=(l+r)>>1;
if(p<=mid) Insert(ls[v],ls[u],l,mid,p);
else Insert(rs[v],rs[u],mid+1,r,p);
}
int Query(int a,int b,int c,int d,int l,int r,int K){
if(l==r) return l;
int mid=(l+r)>>1,lcnt=cnt[ls[a]]+cnt[ls[b]]-cnt[ls[c]]-cnt[ls[d]];
if(K<=lcnt) return Query(ls[a],ls[b],ls[c],ls[d],l,mid,K);
else return Query(rs[a],rs[b],rs[c],rs[d],mid+1,r,K-lcnt);
}
void Reset(){
for(int u=1;u<=N;u++){
bel[u]=u; siz[u]=1; dep[u]=1;
Insert(rt[fa[u][0]],rt[u],1,tnt,A[u]);
}
}
}DT;
void read(int &x){
static int f; static char ch;
x=0; f=1; ch=getchar();
while(ch<'0'||'9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
x=x*f;
}
int find(int u){
return u==bel[u]?u:bel[u]=find(bel[u]);
}
void dfs(int u,int dad){
memset(fa[u],0,sizeof(fa[u]));
dep[u]=dep[dad]+1; fa[u][0]=dad;
for(int k=1;k<=18&&fa[fa[u][k-1]][k-1];k++)
fa[u][k]=fa[fa[u][k-1]][k-1];
DT.Insert(DT.rt[dad],DT.rt[u],1,tnt,A[u]);
for(int i=E.head[u],v;i;i=E.nxt[i]){
v=E.to[i]; if(v==dad) continue;
dfs(v,u);
}
}
void merge(int u,int v){
static int fu,fv;
fu=find(u); fv=find(v);
if(siz[fu]<siz[fv]) swap(u,v),swap(fu,fv);
E.Adde(u,v); bel[fv]=fu; siz[fu]+=siz[fv];
dfs(v,u);
}
int LCA(int x,int y){
if(dep[x]>dep[y]) swap(x,y);
for(int k=18;k>=0;k--)
if(dep[fa[y][k]]>=dep[x]) y=fa[y][k];
if(x==y) return x;
for(int k=18;k>=0;k--)
if(fa[x][k]!=fa[y][k]) x=fa[x][k],y=fa[y][k];
return fa[x][0];
}
void solve(){
int lastANS=0,a,b,c,d,rta,rtb,rtc,rtd,K; char ch;
for(int i=1;i<=T;i++){
scanf(" %c",&ch);
read(a); read(b);
a^=lastANS; b^=lastANS;
if(ch=='Q'){
read(K); K^=lastANS;
c=LCA(a,b); d=fa[c][0];
rta=DT.rt[a]; rtb=DT.rt[b];
rtc=DT.rt[c]; rtd=DT.rt[d];
lastANS=tmp[DT.Query(rta,rtb,rtc,rtd,1,tnt,K)];
printf("%d\n",lastANS);
}
else merge(a,b);
}
}
int main(){
int testcase;read(testcase);
read(N); read(M); read(T);
for(int i=1;i<=N;i++)
read(A[i]),tmp[i]=A[i];
sort(tmp+1,tmp+N+1);
tnt=unique(tmp+1,tmp+N+1)-tmp-1;
for(int i=1;i<=N;i++)
A[i]=lower_bound(tmp+1,tmp+tnt+1,A[i])-tmp;
DT.Reset();
for(int i=1,u,v;i<=M;i++){
read(u); read(v);
merge(u,v);
}
solve();
return 0;
}

●BZOJ 3123 [Sdoi2013]森林的更多相关文章

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

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

  2. bzoj 3123: [Sdoi2013]森林(45分暴力)

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

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

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

  4. BZOJ 3123 [SDOI2013] 森林 - 启发式合并 主席树

    Description 给你一片森林, 支持两个操作: 查询$x$到$y$的$K$大值,  连接两棵树中的两个点 Solution 对每个节点$x$动态开权值线段树, 表示从$x$到根节点路径上权值出 ...

  5. BZOJ 3123 SDOI2013 森林

    首先对于查询操作就是裸的COT QAQ 在树上DFS建出主席树就可以了 对于连接操作,我们发现并没有删除 所以我们可以进行启发式合并,每次将小的树拍扁插入大的树里并重构即可 写完了之后第一个和第二个点 ...

  6. bzoj 3123 [Sdoi2013]森林(主席树,lca,启发式合并)

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

  7. bzoj 3123 [Sdoi2013]森林(主席树+启发式合并+LCA)

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

  8. 3123: [Sdoi2013]森林

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

  9. 【BZOJ】3123: [Sdoi2013]森林

    题解 ------------------ 我莫不是一个智障吧 我把testdata的编号 当成数据组数读进来 我简直有毒 以为哪里写错了自闭了好久 实际上这题很简单,只要愉悦地开个启发式合并,然后每 ...

随机推荐

  1. 201621123060《JAVA程序设计》第十四周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结与数据库相关内容. 2. 使用数据库技术改造你的系统 2.1 简述如何使用数据库技术改造你的系统.要建立什么表?截图你的表设计. 用 ...

  2. mahony互补滤波器C编程

    //gx...分别为重力加速度在三个轴向的分力 由加速度计测得 //ax...分别为角速度在三个轴向的角速度 由陀螺仪测得 //最后得到最终滤波完毕的x.y.z方向的角度值(°) void IMUup ...

  3. MySql数据库的常用命令

    1.连接Mysql 连接本地的mysql数据库 :   mysql -u root -p    (回车之后会提示输入密码) 连接远程主机的mysql数据库 : 假设远程主机的IP为:110.110.1 ...

  4. 第一周-JAVA基本概念

    1. 本周学习总结 本周学习内容: 1.JAVA的发展 2.JDK,JVM,JRE, 3.掌握JAVA的组成结构 4.掌握使用简单的编译器写javac与java命令, 关键概念之间的联系: JVM:将 ...

  5. 关于搭建MyBatis框架(二)

    由于在[关于使用Mybatis的使用说明(一)http://www.cnblogs.com/zdb292034/p/8675766.html]中存在不太完善地方,通过此片文档进行修订: 阅读指南:(1 ...

  6. 使用静态基类方案让 ASP.NET Core 实现遵循 HATEOAS Restful Web API

    Hypermedia As The Engine Of Application State (HATEOAS) HATEOAS(Hypermedia as the engine of applicat ...

  7. Python爬虫基本原理

    爬虫基本原理 1. 什么是爬虫 请求网站并提取数据的自动化程序. 2. 爬虫基本流程 发起请求 通过HTTP库向目标站点发起请求,即发送一个Request,请求可以包含额外的headers等信息,等待 ...

  8. (干货)微信小程序之上传图片和图片预览

    这几天一直负责做微信小程序这一块,也可以说是边做边学习吧,把自己做的微信小程序的一些功能分享出来,与大家探讨一下,相互学习相互进步. 先看下效果图 只写了一下效果样式的话希望大家不要太在意,下面马路杀 ...

  9. 隐藏Easy UI 中parent.$.modalDialog 的button

    例子: buttons : [ { text : '关闭', handler : function() { parent.$.modalDialog.handler.dialog('close'); ...

  10. JavaScript 重点笔记

    JavaScript 重点笔记 ## 数组 // 必须掌握 - arr.length:获取数组元素的长度 - arr.splice(起始位置,长度):从数组中添加或删除元素. - arr.indexO ...