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;
}