[Sdoi2013]森林

/*
平常这种题很常见的思路就是求出dfs序来,然后每次查询的时候就是在主席树上查询 x+y-lca-fa[lca] 的值就行了。
但是这个题要动态的给森林中加边,还是强制在线的,所以就需要考虑换一种方法来维护这个东西。
首先先dfs出每棵树来,然后对于link操作,可以启发式合并两个主席树。这里我们把主席树维护的dfs序变成维护每个点到根的这条路径。所里link的时候假设我们要把x合到y上,那么我们就边dfs x 这棵树,边用当前点的fa作为历史状态的root来更新当前点的root就行了。求lca的fa数组和deep数组在dfs的时候动态维护就行了。
复杂度: O(nlog2n)
*/
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+5;
const int M=2e7+5;
const int inf=1e9;
int n,m,T,sz,num,ans,val[N],dep[N],siz[N],belong[N],fa[N][20];bool flag[N];
struct edge{int u,v,next;}e[N<<1];int tot,head[N];
int root[N],R[N],sum[M],ls[M],rs[M];
inline int read(){
    int x=0;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x;
}
inline void add(int x,int y){
    e[++tot].v=y;e[tot].next=head[x];head[x]=tot;
    e[++tot].v=x;e[tot].next=head[y];head[y]=tot;
}
void insert(int &k,int last,int l,int r,int pos){
    k=++sz;
    sum[k]=sum[last]+1;
    if(l==r) return ;
    ls[k]=ls[last];
    rs[k]=rs[last];
    int mid=l+r>>1;
    if(pos<=mid) insert(ls[k],ls[last],l,mid,pos);
    else insert(rs[k],rs[last],mid+1,r,pos);
}
void dfs(int x,int f,int now){
    flag[x]=1;siz[x]=1;belong[x]=now;
    for(int i=1;i<20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    insert(root[x],root[f],1,inf,val[x]);
    for(int i=head[x];i;i=e[i].next){
        if(e[i].v!=f){
            fa[e[i].v][0]=x;
            dep[e[i].v]=dep[x]+1;
            dfs(e[i].v,x,now);
            siz[x]+=siz[e[i].v];
        }
    }
}
inline int lca(int a,int b){
    if(dep[a]<dep[b]) swap(a,b);
    int t=dep[a]-dep[b];
    for(int i=0;i<20;i++){
        if(t&(1<<i)){
            a=fa[a][i];
        }
    }
    if(a==b) return a;
    for(int i=19;~i;i--){
        if(fa[a][i]!=fa[b][i]){
            a=fa[a][i];
            b=fa[b][i];
        }
    }
    return fa[a][0];
}
int query(int l,int r,int x1,int x2,int x3,int x4,int pos){
    if(l==r) return l;
    int now=sum[ls[x1]]+sum[ls[x2]]-sum[ls[x3]]-sum[ls[x4]];
    int mid=l+r>>1;
    if(now>=pos) return query(l,mid,ls[x1],ls[x2],ls[x3],ls[x4],pos);
    return query(mid+1,r,rs[x1],rs[x2],rs[x3],rs[x4],pos-now);
}
int main(){
    freopen("forest.in","r",stdin);
    freopen("forest.out","w",stdout);
    T=read();n=read();m=read();T=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1,x,y;i<=m;i++) x=read(),y=read(),add(x,y);
    for(int i=1;i<=n;i++) if(!flag[i]) dfs(i,0,++num),R[num]=i;
    for(int x,y,z,anc;T--;){
        char ch;
        for(ch=getchar();ch!=‘Q‘&&ch!=‘L‘;ch=getchar());
        x=read();y=read();
        x^=ans;y^=ans;
        if(ch==‘Q‘){
            z=read();z^=ans;
            anc=lca(x,y);
            ans=query(1,inf,root[x],root[y],root[anc],root[fa[anc][0]],z);
            printf("%d\n",ans);
        }
        else{
            if(siz[R[belong[x]]]>siz[R[belong[y]]]) swap(x,y);
            add(y,x);
            fa[x][0]=y;
            siz[R[belong[y]]]+=siz[R[belong[x]]];
            dep[x]=dep[y]+1;
            dfs(x,y,belong[y]);
        }
    }
    return 0;
}
/*10
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

#define setfire(name) freopen(#name".in","r",stdin);freopen(#name".out","w",stdout);
#define fre(name) freopen(#name".txt","r",stdin);
#ifdef WIN32
#define LL "%lld"
#else
#define LL "%I64d"
#endif

const int N=1e5+5;
int cas,n,m,t,ans,val[N],tv[N],stack[N],prev[N];bool vis[N];
struct edge{int v,next;}e[N<<1];int tot,head[N];
char s[50];

inline int read(){
    int x=0;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x;
}
inline void add(int x,int y){
    e[++tot].v=y;e[tot].next=head[x];head[x]=tot;
    e[++tot].v=x;e[tot].next=head[y];head[y]=tot;
}
inline void bfs(int S,int T){
    int top=1;stack[top]=S;
    memset(vis,0,(n+2));
    memset(prev,0,(n+2)<<2);
    while(top){
        int x=stack[top--];
        for(int i=head[x];i;i=e[i].next){
            if(!vis[e[i].v]){
                vis[e[i].v]=1;
                prev[e[i].v]=x;
                if(e[i].v==T) return ;
                stack[++top]=e[i].v;
            }
        }
    }
}
inline void calc(int S,int T,int rk){
    tv[0]=0;
    for(int i=T;i!=S;i=prev[i]) tv[++tv[0]]=val[i];tv[++tv[0]]=val[S];
    nth_element(tv+1,tv+rk,tv+tv[0]+1);
    printf("%d\n",ans=tv[rk]);
}
void init(){
    tot=0;
    memset(head,0,(n+2)<<2);
}
int main(){
    freopen("forest.in","r",stdin);
    freopen("forest.out","w",stdout);
    //for(cas=read();init(),cas--;){
        cas=read();
        n=read();m=read();t=read();
        for(int i=1;i<=n;i++) val[i]=read();
        for(int i=1,x,y;i<=m;i++) x=read(),y=read(),add(x,y);
        for(int x,y,z;t--;){
            scanf("%s",s);
            if(s[0]==‘Q‘){
                x=read();y=read();z=read();
                x^=ans;y^=ans;z^=ans;
                bfs(x,y);
                calc(x,y,z);
            }
            else{
                x=read();y=read();
                x^=ans;y^=ans;
                add(x,y);
            }
        }
//    }
    return 0;
} */
时间: 2024-08-11 01:35:38

[Sdoi2013]森林的相关文章

Luogu_P3302 [SDOI2013]森林【题解】主席树 lca 启发式合并

# Luogu_P3302 [SDOI2013]森林 主席树,启发式合并,lca luogu题面 求树上路径的第k大,树之间还有合并. 明显是主席树再加合并. 先说链上第k大,其实就是$Tx+Ty-Tlca-Tlcafa$ $T$表示权值线段树. 主席树维护的是从根节点到当前节点的前缀和. ask的代码如下: inline int ask(int x,int y,int lcc,int lcf,int l,int r,int k){ if(l==r) return b[l]; int lz=su

[SDOI2013]森林(树上主席树)

[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,

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

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

Luogu3302 [SDOI2013]森林

题目蓝链 Description 给你一个森林,你需要支持两个操作: 查询一条路径上第\(k\)小的权值是多少 连接两个点 强制在线 Solution 我们一开始看到这道题,一定会想什么LCT套主席树 乱编的 其实我们只需要主席树就可以了.我们发现这题只需要连边,不需要断边.所以我们可以启发式合并,暴力维护较小的那部分的主席树和倍增数组 时间复杂度\(\mathcal{O}(n \log^2 n)\) 如果你还不会用主席树维护链上第\(k\)大,请右转 传送门 Solution #include

【主席树 启发式合并】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 对于每

P3302 [SDOI2013]森林

传送门 看到森林有合并首先会想到 $LCT$ ,然后发现链上第 $K$ 小不可维护 感觉 $LCT$ 只维护合并也有点大材小用了,考虑合并时直接启发式合并就可以不用 $LCT$ 然后求第 $K$ 小显然考虑主席树 对每个节点维护一个主席树,维护它到树根这的一段区间,那么当前节点的线段树可以直接借用父节点的线段树 并且因为主席树是可加减的,设节点 $x$ 的线段树为 $T[x]$,那么询问是就是在 $T[x]+T[y]-T[lca(x,y)]-T[fa[lca(x,y)]]$ 上面跑 每次启发式合

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

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

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

题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,初始的时候lastans为0. 对于一个输入的操作Q x y k

「Luogu P3302」[SDOI2013]森林

给出一片森林,每个点有一个权值,要求支持动态连边,并回答任意两点间第 k 小权值,强制在线.\((1\le N,M,T \le 8\times 10^4)\) Luogu 分析 求第 k 小权值,这个肯定是用主席树了,但连边该怎么办?LCT?可我不会. 我们可以用启发式合并的方法,连边也就是合并两棵树,我们每次将较小的树连到较大的树上去,更新信息就暴力 dfs 较小树中的每一个点就好了. 代码 #include <bits/stdc++.h> #define N 80003 #define D