题链:

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. 实验四:Android 开发基础

    实验四:实验报告 课程:程序设计与数据结构 班级: 1623 姓名: 张旭升 学号:20162329 指导教师:娄嘉鹏 王志强 实验日期:5月26日 实验密级: 非密级 预习程度: 已预习 必修/选修 ...

  2. 局域网下访问其他计算机搭建的django网页

    1.修改工程目录下的setting.py 文件 ALLOWED_HOSTS = ['*'] #*表示允许访问的ip 如果是添加* 则允许所有同局域网环境的主机访问 2.在完成的django工程下运行以 ...

  3. iOS 11 导航栏 item 偏移问题 和 Swift 下 UIButton 设置 title、image 显示问题

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  4. 04_Linux目录文件操作命令1(mv ls cd...)_我的Linux之路

    上一节已经给大家讲了Linux的目录结构,相信大家已经对Linux的整个目录结构有所了解 现实中,服务器(包含Linux,Unix,windows server)一般都摆放在机房里,因为一个机房摆放了 ...

  5. JAVA_SE基础——14.循环结构语句

    建议有些基础的同学阅读,0基础可能会有些困难(最好看正文配合基础课本的例子) 所谓循环语句主要就是在满足条件的情况下反复执行某一个操作.Java提供了3种常用的循环语句,分别为for循环语句.whil ...

  6. php后台的在控制器中就可以实现阅读数增加

    $smodel=M('Sswz');$smodel->where($map)->setInc('view' ,1);php后台的在控制器中就可以实现阅读数增加前台不需要传值

  7. Python内置函数(27)——range

    英文文档: range(stop) range(start, stop[, step]) Rather than being a function, range is actually an immu ...

  8. sts中maven

    建立一个maven web的工程 网上有很多关于maven的下载,配置等,我这里就不多说了. 下面介绍主要介绍关于在sts中建立一个maven时最开始出现的错误问题. 创建maven工程 file-& ...

  9. 新概念英语(1-101)A Card From Jimmy

    Lesson 101 A card from Jimmy 吉米的明信片 Listen to the tape then answer this question. Does Grandmother s ...

  10. hadoop2.7.3+spark2.1.0+scala2.12.1环境搭建(2)安装hadoop

    一.依赖安装 安装JDK 二.文件准备 hadoop-2.7.3.tar.gz 2.2 下载地址 http://hadoop.apache.org/releases.html 三.工具准备 3.1 X ...