1. 程式人生 > 實用技巧 >D. Graph and Queries 並查集+線段樹+kruskal重構樹

D. Graph and Queries 並查集+線段樹+kruskal重構樹

D. Graph and Queries 並查集+線段樹+kruskal重構樹

題目大意:

給你n個節點,每一個節點有一個權值,m條邊,q次查詢,有兩種查詢

  • 1 v 表示查v所在的連通塊的最大值
  • 2 x 表示第x這條邊刪去

題解:

這個型別求連通塊的最大值和刪邊操作的題目可以用kruskal重構樹做。

如果我想求一個連通塊的最大值,那麼我希望可以讓這個連通塊變成一個連續的區間,那麼我就可以轉化成求區間最大值即可,但是有這個刪邊操作,所以我需要構造一種方法使得每次查連通塊都是查一個連續的區間,對於刪邊這個操作只是把連通塊分裂,這個可以考慮一棵樹,因為一個圖連通,那麼變成一顆生成樹之後一定在同一棵樹,所以可以用並查集維護是否在同一棵樹,並新建一棵樹,那麼同一棵的所有子節點一定是連通的,所以dfs序也是連續的,但問題是有刪邊操作,所以如果直接把圖轉化成樹肯定是有問題的,因為刪完邊之後不能保證分裂出來的兩個連通塊dfs序是連通的。

所以要找一種方法來構造這個生成樹,保證刪邊之後分裂出來的兩個連通塊dfs序是連續的。

這個可以用kruskal重構樹,可以上網稍微學習一下,其實就是 把邊權拆成一個點。

那麼我們先把刪完之後的圖建成一顆樹,然後倒著繼續建樹,那麼按照刪除的順序刪邊,每次刪完得到的兩個連通塊的dfs序是連續的。

#include <bits/stdc++.h>
#define lc (id<<1)
#define rc (id<<1|1)
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn = 6e5+10;
int p[maxn],a[maxn],b[maxn],flag[maxn];
int qur[maxn],id[maxn],f[maxn],tot;
int head[maxn],to[maxn<<1],nxt[maxn<<1],cnt,now;
void add(int u,int v){
    ++cnt,to[cnt] = v,nxt[cnt] = head[u],head[u] = cnt;
    ++cnt,to[cnt] = u,nxt[cnt] = head[v],head[v] = cnt;
}
int find(int x){
    return f[x]==x?x:f[x]=find(f[x]);
}
void unite(int x,int y){
    x = find(x),y = find(y);
    if(x==y) return ;
    ++tot,add(x,tot),add(y,tot);
    f[tot] = f[x] = f[y] = tot;
}
bool same(int x,int y){
    return find(x)==find(y);
}
int el[maxn],er[maxn],rk[maxn];
void dfs(int u,int pre){
    el[u] = ++now,rk[now] = u;
    for(int i=head[u];i;i=nxt[i]){
        int v = to[i];
        if(v == pre) continue;
        dfs(v,u);
    }
    er[u] = now;
}
int maxs[maxn<<2];
int Max(int i,int j){
    return p[rk[i]]>p[rk[j]]?i:j;
}
void push_up(int id){
    maxs[id] = Max(maxs[lc],maxs[rc]);
}
void build(int id,int l,int r){
    if(l==r){
        maxs[id] = l;
        return ;
    }
    int mid = (l+r)>>1;
    build(lc,l,mid);
    build(rc,mid+1,r);
    push_up(id);
}
 
int query(int id,int l,int r,int x,int y){
    if(x<=l&&y>=r) return maxs[id];
    int mid = (l+r)>>1,ans = 0;
    if(x<=mid) ans = Max(ans,query(lc,l,mid,x,y));
    if(y>mid) ans = Max(ans,query(rc,mid+1,r,x,y));
    return ans;
}
void update(int id,int l,int r,int pos,int val){
    if(l==r){
        p[rk[l]] = 0;        
        // maxs[id] = 0;
        return ;
    }
    int mid = (l+r)>>1;
    if(pos<=mid) update(lc,l,mid,pos,val);
    if(pos>mid) update(rc,mid+1,r,pos,val);
    push_up(id);
}
int tree[maxn];
int main(){
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    tot = n,cnt = 0,now = 0;
    p[0] = 0,rk[0] = 0;
    for(int i=1;i<=n;i++) scanf("%d",&p[i]),f[i] = i;
    for(int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]),flag[i] = false;
    for(int i=1;i<=q;i++){
        scanf("%d%d",&qur[i],&id[i]);
        if(qur[i]==2) flag[id[i]] = true;
    }
    for(int i=1;i<=m;i++){
        if(!flag[i]) unite(a[i],b[i]);
    }
    for(int i=q;i>=1;i--){
        int x = id[i];
        if(qur[i]==2){
            if(same(a[x],b[x])) continue;
            unite(a[x],b[x]);
        }
        else tree[i] = find(x);
    }
    for(int i=1;i<=tot;i++){
        if(f[i]==i) dfs(i,0);
    }
    build(1,1,now);
    for(int i=1;i<=q;i++){
        if(qur[i]==1){
            int rt = tree[i];
            int ans = query(1,1,now,el[rt],er[rt]);
            printf("%d\n", p[rk[ans]]);
            update(1,1,now,ans,0);
        }
    }
    return 0;
}