题目链接:传送门
题目思路:思路参考博客 https://www.cnblogs.com/EchoZQN/p/13804989.html
对于这种题,首先会思考如何转化删边,常用的就是把所有的查询和修改作逆序处理,然后并查集求解,但是这道题由于有修改(查询一个最大值后删除),因此不能采用这个方法。
那么这道题可以采用类似于kruskal 生成树重构的方法建图,那么对于每一个连通块的所有点都是位于同一棵子树的(子树的根是连通块的祖先节点),不论是删边前的大连通块,还是删边后可能形成的小连通块,这样以来也能保证每一个连通块的所有dfs序是连续的,那么就可以用线段树来维护最值了。
代码:
#include<bits/stdc++.h> #pragma GCC optimize(2) using namespace std; typedef long long LL; typedef unsigned long long uLL; typedef pair<int,int> pii; typedef pair<LL,LL> pLL; typedef pair<double,double> pdd; const int N=5e5+5; const int M=8e5+5; const int inf=0x3f3f3f3f; const LL mod=1e8+7; const double eps=1e-8; const long double pi=acos(-1.0L); #define ls (i<<1) #define rs (i<<1|1) #define fi first #define se second #define pb push_back #define eb emplace_back #define mk make_pair #define mem(a,b) memset(a,b,sizeof(a)) LL read() { LL x=0,t=1; char ch; while(!isdigit(ch=getchar())) if(ch=='-') t=-1; while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } return x*t; } int n,m,q,p[N],f[N],vis[N],id,pp[N]; pii a[N],b[N]; int rt[N],c[N<<2],in[N],out[N],rk[N],now; vector<int> e[N]; int getf(int x) { return f[x]==x?f[x]:f[x]=getf(f[x]); } void build(int i,int l,int r) { if(l==r) return (void)(c[i]=p[rk[l]]); int mid=l+r>>1; build(ls,l,mid); build(rs,mid+1,r); c[i]=max(c[ls],c[rs]); } void update(int i,int l,int r,int pos,int x) { if(l==r) return (void)(c[i]=x); int mid=l+r>>1; if(pos<=mid) update(ls,l,mid,pos,x); else update(rs,mid+1,r,pos,x); c[i]=max(c[ls],c[rs]); } int query(int i,int l,int r,int ll,int rr) { if(ll<=l&&r<=rr) return c[i]; int mid=l+r>>1,t1=0,t2=0; if(mid>=ll) t1=query(ls,l,mid,ll,rr); if(mid<rr) t2=query(rs,mid+1,r,ll,rr); return max(t1,t2); } void dfs(int u) { // printf("u = %d ",u); in[u]=++id; rk[id]=u; for(auto v:e[u]) dfs(v); out[u]=id; } int main() { n=read(),m=read(),q=read(); for(int i=1;i<=n;i++) p[i]=read(); for(int i=1;i<=n;i++) pp[p[i]]=i; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++) { int x=read(),y=read(); a[i]=mk(x,y); } for(int i=1;i<=q;i++) { int x=read(),y=read(); b[i]=mk(x,y); if(x==2) vis[y]=1; } now=n; for(int i=1;i<=m;i++) { if(vis[i]) continue; int x=getf(a[i].fi),y=getf(a[i].se); if(x==y) continue; f[now]=++now; f[x]=f[y]=now; e[now].eb(x); e[now].eb(y); } for(int i=q;i;i--) { if(b[i].fi==2) { int j=b[i].se; int x=getf(a[j].fi),y=getf(a[j].se); if(x==y) continue; f[now]=++now; f[x]=f[y]=now; e[now].eb(x); e[now].eb(y); } else rt[i]=getf(b[i].se);//对于i时刻来说 1~i-1这段时间 边已经断掉了,此时祖先代表了这个小的连通块;也可以给每个点加入时间权,自底向上权值不上升,采用树上倍增即可 } for(int i=1;i<=now;i++) if(i==f[i]) dfs(i); build(1,1,now); for(int i=1;i<=q;i++) { if(b[i].fi==2) continue; //printf("rt = %d ",rt[i]); int ans=query(1,1,now,in[rt[i]],out[rt[i]]); printf("%d ",ans); //printf("%d %d ",ans,in[pp[ans]]); if(ans) update(1,1,now,in[pp[ans]],0); } return 0; }