D. Graph and Queries 並查集+線段樹+kruskal重構樹
阿新 • • 發佈:2020-10-12
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; }