【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时间复杂度为O(n*log),因为每个的节点只会作为小的部分合并,因此他所在的一小部分至少变大2倍,对于每一个节点他作为被合并方最多log次,因此其复杂度为O(n*log),而这个是绝对跑不满还差很多的,我们视他为无常数就好了,当然这一切都是建立在无拆分操作的基础之上,只要有了拆分启发式合并的复杂度就是暴力了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 80001
using namespace std;
inline int read(){
    register int sum=0;register char ch=getchar();
    while(ch<‘0‘||ch>‘9‘)ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘)sum=(sum<<1)+(sum<<3)+ch-‘0‘,ch=getchar();
    return sum;
}
int n,m,T,fa[MAXN],size[MAXN],f[MAXN][20];
struct Via{
    int to,next;
}c[MAXN<<1];
int head[MAXN],t;
struct Seg_Tree{
    Seg_Tree *ch[2];
    int size;
}*root[MAXN],*null;
int pos[MAXN],a[MAXN],deep[MAXN];
int Hash[MAXN],len;
bool v[MAXN];
//*********************Define********************//
inline int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline void Unit_to_(int x,int y){
    fa[find(x)]=find(y);
}
//*******************Union_Find_Sets*************//
inline void add(int x,int y){
    c[++t].to=y,c[t].next=head[x],head[x]=t;
}
//********************Maintain_Tree**************//
int comp(const int x,const int y){
    return a[x]<a[y];
}
inline void HASH(){
    sort(pos+1,pos+n+1,comp);
    for(register int i=1;i<=n;i++)
        if(i==1||a[pos[i]]!=a[pos[i-1]])Hash[++len]=a[pos[i]],a[pos[i]]=len;
        else a[pos[i]]=len;
}
//********************Hash**********************//
void ins(Seg_Tree *&p,Seg_Tree *last,int l,int r,int key){
    p=new Seg_Tree,*p=*last,++p->size;if(l==r)return;
    if(key<=((l+r)>>1))ins(p->ch[0],last->ch[0],l,((l+r)>>1),key);
    else ins(p->ch[1],last->ch[1],((l+r)>>1)+1,r,key);
}
void Dfs_Build(int x,int Fa){
    deep[x]=deep[Fa]+1;v[x]=1;
    for(register int i=1;i<20;i++)f[x][i]=f[f[x][i-1]][i-1];
    ins(root[x],root[Fa],1,len,a[x]);
    for(register int i=head[x];i;i=c[i].next)
        if(c[i].to!=Fa) f[c[i].to][0]=x,Dfs_Build(c[i].to,x);
}
int query(Seg_Tree *A1,Seg_Tree *A2,Seg_Tree *B1,Seg_Tree *B2,int l,int r,int k){
    if(l==r)return l;register int sum=A1->ch[0]->size-A2->ch[0]->size+B1->ch[0]->size-B2->ch[0]->size;
    if(sum>=k)return query(A1->ch[0],A2->ch[0],B1->ch[0],B2->ch[0],l,((l+r)>>1),k);
    else return query(A1->ch[1],A2->ch[1],B1->ch[1],B2->ch[1],((l+r)>>1)+1,r,k-sum);
}
void Del(Seg_Tree *p,Seg_Tree *last){
    if(p==last)return;
    Del(p->ch[0],last->ch[0]),Del(p->ch[1],last->ch[1]);
    delete p;
}
void Delete(int x,int fa){
    for(register int i=head[x];i;i=c[i].next)
        if(c[i].to!=fa)Delete(c[i].to,x);
    Del(root[x],root[fa]),root[x]=null;
}
//********************Seg_Tree******************//
inline int Lca(int x,int y){
    if(deep[x]<deep[y])x^=y^=x^=y;
    register int k=deep[x]-deep[y];
    for(register int i=0;(1<<i)<=k;i++)
        if(k&(1<<i)) x=f[x][i];
    if(x==y)return x;
    for(register int i=19;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
//*********************LCA*********************//
inline void Init(){
    scanf("%*d"),n=read(),m=read(),T=read();
    null=new Seg_Tree,null->ch[0]=null->ch[1]=null,null->size=0,root[0]=null;
    for(register int i=1;i<=n;i++)a[i]=read(),pos[i]=i,root[i]=null,size[i]=1,fa[i]=i;HASH();
    for(register int i=1,x,y;i<=m;i++)x=read(),y=read(),add(x,y),add(y,x),size[find(y)]+=size[find(x)],Unit_to_(x,y);
    for(register int i=1;i<=n;i++)if(!v[i])Dfs_Build(find(i),0);
}
inline void Work(){
    register int x,y,k,lca,ans=0;register char s[1];
    while(T--){
        scanf("%s",s);
        if(s[0]==‘Q‘){
            x=read()^ans,y=read()^ans,k=read()^ans,lca=Lca(x,y);
            printf("%d\n",(ans=Hash[query(root[x],root[lca],root[y],root[f[lca][0]],1,len,k)]));
        }else{
            x=read()^ans,y=read()^ans;if(size[find(x)]>size[find(y)])x^=y^=x^=y;
            Delete(find(x),0),size[find(y)]+=size[find(x)],Unit_to_(x,y);
            f[x][0]=y,add(x,y),add(y,x),Dfs_Build(x,y);
        }
    }
}
//*********************Main********************//
int main(){
    Init();
    Work();
    return 0;
}
时间: 2024-08-06 20:08:43

【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并的相关文章

[bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值. 接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为"Q x y k"或者"L x y ",其含义见题目描述部分. Output 对于每一个第一类

bzoj3123: [Sdoi2013]森林 主席树+启发式合并

思博题加强版,还是思博题,RT,没了. 内存log^2,写了回收的话可以少个log. lca不能用树剖了好悲伤(IoI),讨厌倍增. 没有1A好不爽啊啊啊,最近写思博题只有一道1A的是要退役的节奏(@[email protected]) #include<cstdio> #include<algorithm> #define N 80005 #define M (l+r>>1) using namespace std; char o[2]; int k,m,q,s,t,

【主席树 启发式合并】bzoj3123: [Sdoi2013]森林

小细节磕磕碰碰浪费了半个多小时的时间 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值.  接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分. Output 对于每

BZOJ 3123 SDOI2013 森林 可持久化线段树+倍增LCA+启发式合并

题目大意:给定一棵森林,每个点有权值,提供两种操作: 1.查询两点间路径上第k小的权值 2.将两个点之间连一条边 保证连接后仍是一座森林 可持久化线段树部分同Count On A Tree 只是这道题加了个连接操作 对于连接操作我们要用到启发式合并 就是把小的那棵树暴力重建 很简单的一个操作但是可以证明是均摊O(nlogn)的 大小我用了并查集 其实记录根就可以了 此外本题的多组数据是在逗比 记住testcase恒等于1就是了 NND我倍增LCA又写错了0.0 预处理时居然从大往小写的0.0 样

【主席树启发式合并】【P3302】[SDOI2013]森林

Description 给定一个 \(n\) 个节点的森林,有 \(Q\) 次操作,每次要么将森林中某两点联通,保证操作后还是个森林,要么查询两点间权值第 \(k\) 小,保证两点联通.强制在线. Limitation \(1~\leq~n,~Q~\leq~80000\) Solution 考虑有连边还有查询链上第 \(k\) 大,于是要么用 LCT,要么用主席树. 考虑如果用 LCT 的话,并不能快速的维护两点间链的信息(其实感觉在access的时候乱搞一下有希望在多一个 \(\log\) 的

p3302 [SDOI2013]森林(树上主席树+启发式合并)

对着题目yy了一天加上看了一中午题解,终于搞明白了我太弱了 连边就是合并线段树,把小的集合合并到大的上,可以保证规模至少增加一半,复杂度可以是\(O(logn)\) 合并的时候暴力dfs修改倍增数组和维护主席树即可 然后树上主席树就是维护节点到根节点的信息即可, 询问链上的第k大时,画图后可以发现维护一个rootx,rooty,rootlca和rootfalca即可解决问题 注意空间要开够 然后注意细节,没了 #include <cstdio> #include <cstring>

bzoj 3123: [Sdoi2013]森林

如果题号没记错的话,2588是一个树上的主席树查询.这个题就是多了个合并而已.每一次都把小的合并到大的上就好了(所谓启发式2333) (主席树真是个好东西2333) (上部分为本蒟蒻不知道为什么RE到死都RE的代码,,,挖坑) (个人感觉主席树这种东西里离散不离散没什么区别的(常数而已),毕竟是log的,大个几次方就是多了个常数而已) 1 /*#include<bits/stdc++.h> 2 #define N 100005 3 #define M 40000005 4 #define LL

主席树+启发式合并(LT) BZOJ3123

好久没做题了,写道SBT又RE又T 查询:主席树裸题. 修改:对于两个树合并重建小的树. 注意fa[x][i]重新计算时要清空 #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; inline int read() { char c=getchar();int x=0,sig=1; for(;!isdigit(c);c=get

BZOJ3123LCA+主席树+启发式合并

d穆善蓟8悼z旱移7http://www.zcool.com.cn/collection/ZMTg1MjE5MDg=.html 自9B是O焕n偷儆裙1http://www.zcool.com.cn/collection/ZMTg1MjE5NTY=.html 088酥1FVL斗稳7http://www.zcool.com.cn/collection/ZMTg1MjIwNjQ=.html 271U59mYG寂灾http://www.zcool.com.cn/collection/ZMTg1MjIxM