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

传送门
写这题需要点亮的技能有:LCA(倍增orLCT求法),启发式合并,主席树.
先讲暴力的算法,我们可以用并查集维护这个森林,当两个点连通的时候,我们暴力的去遍历其中一棵树,建立新的LCA和主席树两种结构,这个很好搞定.问题是,这样的时间复杂度和空间复杂度都不可承受,下面介绍启发式合并的思想优化这个算法.
启发式合并这个东西,是一个很神奇的思想,因为这是一个森林,而且点集的规模是只增不减的,这类题目我们可以用并查集+启发式合并,优化暴力算法.考虑合并的时候把小的点集合并到大的点集里,那么这个合并复杂度会从暴力的O(N)优化O到(log(N)).具体证明大家可以上网找找,这个复杂度感性上应该很容易接受.
至于合并的时候更新主席树的操作比较简单,倍增LCA也容易,具体就是只拓展点集比较小的那个点重新求一遍倍增数组就好了.
代码:

#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#define lson(i) tree[i].l
#define rson(i) tree[i].r
#include <bits/stdc++.h>

using namespace std;
const int N = 1e5+10;
const int maxlen = 25;

inline void read(int &a){
    
    
    int x = 0,f=1;char ch = getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){
    
    x=x*10+ch-'0';ch=getchar();}
    a = x*f;
}
struct Tree{
    
    
    int l,r,sum;
}tree[N*180];
int n,m,t,tot,a[N],nxt[N*4],dp[maxlen+1][N],d[N],head[N],to[N*4],ct,fa[N],siz[N],root[N],vis[N];
void addedge(int x,int y){
    
    
    nxt[++ct] = head[x];head[x] = ct;to[ct] = y;
}
void prework(int s){
    
    
    queue<int> q;
    q.push(s);
    d[s] = d[dp[0][s]]+1;
    while(q.size()){
    
    
        int u = q.front();
        q.pop();
        for(int i = head[u];i;i = nxt[i]){
    
    
            int y = to[i];
            if(y == dp[0][u]) continue;
            dp[0][y] = u;
            d[y] = d[u] + 1;
            fir(k,1,maxlen)
                dp[k][y] = dp[k-1][dp[k-1][y]];
            q.push(y);
        }
    }
}

int lca(int x,int y){
    
    
    if(d[x] > d[y]) return lca(y,x);
    afir(i,maxlen,0)
        if(d[dp[i][y]] >= d[x]) y = dp[i][y];
    if(x == y) return x;
    afir(i,maxlen,0)
        if(dp[i][x] != dp[i][y]) x = dp[i][x],y = dp[i][y];
    return dp[0][x];
}

int find(int x){
    
    
    int r = x,i=x,j=x;while(fa[r] != r) r = fa[r];while(i!=fa[i]) j = fa[i],fa[i] = r,i = j;return r;
}
void add(int pre,int &now,int l,int r,int p){
    
    
    now = ++tot;
    tree[now] = tree[pre];
    tree[now].sum ++;
    if(l >= r) return;
    int mid = l + r >> 1;
    if(p <= mid) add(lson(pre),lson(now),l,mid,p);
    else add(rson(pre),rson(now),mid+1,r,p);
}
int query(int l,int r,int p,int q,int gra,int fa,int k){
    
    
    if(l >= r) return l;
    int mid = l + r >> 1;
    int sum = tree[lson(q)].sum + tree[lson(p)].sum - tree[lson(gra)].sum - tree[lson(fa)].sum;
    if(k <= sum) return query(l,mid,lson(p),lson(q),lson(gra),lson(fa),k);
    else return query(mid+1,r,rson(p),rson(q),rson(gra),rson(fa),k-sum);
}
void dfs(int u){
    
    
    add(root[dp[0][u]],root[u],0,1e9+10,a[u]);
    for(int i = head[u];i;i=nxt[i]){
    
    
        int y = to[i];
        if(y == dp[0][u]) continue;
        dfs(y);
    }
}
void unit(int x,int y){
    
    
    int fx = find(x),fy = find(y);
    siz[fy] += siz[fx];
    siz[fx] = 0;
    fa[fx] = fy;
    dp[0][x] = y;
    d[x] = d[y]+1;
    fir(k,1,maxlen)
        dp[k][x] = dp[k-1][dp[k-1][x]];
    prework(x);
    dfs(x);
}
int main(){
    
    
    int test;
    read(test);read(n);read(m);read(t);
    fir(i,1,n){
    
    
        read(a[i]),fa[i] = i,siz[i] = 1;
        add(root[0],root[i],0,1e9+10,a[i]);
        d[i] = 1;
    }
    fir(i,1,m){
    
    
        int x,y;
        read(x);read(y);
        int fx = find(x),fy = find(y);
        addedge(x,y);addedge(y,x);
        if(fx == fy) continue;
        if(siz[fx] <= siz[fy]) unit(x,y);
        else unit(y,x);
    }
    int lastans = 0;
    fir(i,1,t){
    
    
        char op;
        int x,y,z;
        while(op = getchar()) if(isalpha(op)) break;
        if(op == 'Q'){
    
    
            read(x);read(y);read(z);
            x = x^lastans,y = y^lastans,z = z^lastans;
            printf("%d\n",lastans = query(0,1e9+10,root[x],root[y],root[lca(x,y)],root[dp[0][lca(x,y)]],z));
        }
        else{
    
    
            read(x);read(y);
            x = x^lastans,y = y^lastans;
            int fx = find(x),fy = find(y);
            addedge(x,y);addedge(y,x);
            if(fx == fy) continue;
            if(siz[fx] <= siz[fy]) unit(x,y);
            else unit(y,x);
        }
    }
    
    return 0;
}    

猜你喜欢

转载自blog.csdn.net/weixin_45590210/article/details/108839686