ECF1417F/CF1416D-Graph and Queries
Graph and Queries
题目大意:
给一个\(n\)(1<=n<=2e5)个点,\(m\)(1<=m<=3e5)条边的无向图。有q(1<=q<=5e5)组询问。询问的格式有两种
- 1 v 输入1可以到达的点的最大值,并将此值赋0
- 2 v 删除第v条边
题目要求对于每一个第一类的询问输出所查询到的值。
问题分析:
对于图上最值查询问题可以考虑将其转化为区间查询。因为树上的最值查询利用dfs序容易转化为区间查询。但是很显然,题目中没有给出一颗树,查询的对象也不是某结点的子树。而且不仅伴随和点值更新,还伴随着边的更新。
当遇到集合相关的问题时,很自然的可以想到并查集。但并查集只支持合并的操作,不支持删除,在这个题目中只有删除操作。可以换个方向想,把删除操作倒过来做就是合并操作了。认真思考可以发现,在合并的过程中可以将题目中的图转化成满足下面条件的一棵树:
- 每一棵子树对应合并过程中某一时刻的一个连通块。
具体做法是在合并\((x,y)\)时新建一个结点\(v\),建立两条边\((v,x)\),\((v,y)\),并且在并查集中将使par[find(x)]=v,par[find(y)]=v;那么此时以v为根的子树就代表了在(x,y)连通之后x,y所在联通块。同时对于每一个第一类的询问,在合并之后,也要转化为对v的询问(或者在某一合并过程中将v合并成一个更大的子树u,那就是对u的询问)。
处理询问的过程可以用dfs序与线段树解决。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int>pii;
const int MAX_N=2e6+20;
const int INF=0x3f3f3f3f;
int n,m,q;
int arr[MAX_N];
pii e[MAX_N],que[MAX_N];
int del[MAX_N];
int par[MAX_N];
int tin[MAX_N],tout[MAX_N];
int tree[MAX_N*4];
vector<int>V[MAX_N];
int vis[MAX_N];
int find_p(int x){
if(par[x]==x)return x;
else{
return par[x]=find_p(par[x]);
}
}
void unite(int x,int y){
x=find_p(x),y=find_p(y);
if(x==y)return;
n++;
par[n]=n;
par[x]=n,par[y]=n;
V[n].push_back(x),V[n].push_back(y);
// V[x].push_back(n),V[y].push_back(n);
}
void update(int nod,int l,int r,int k,int v){
if(l==r){
tree[nod]=v;
return;
}
int mid=(l+r)/2;
if(k<=mid){
update(nod*2,l,mid,k,v);
}
else{
update(nod*2+1,mid+1,r,k,v);
}
tree[nod]=max(tree[nod*2],tree[nod*2+1]);
}
int query(int nod,int l,int r,int ll,int rr){
if(l==ll&&r==rr){
return tree[nod];
}
int mid=(l+r)/2;
if(mid>=rr){
return query(nod*2,l,mid,ll,rr);
}
else if(mid<ll){
return query(nod*2+1,mid+1,r,ll,rr);
}
else{
return max(query(nod*2,l,mid,ll,mid),query(nod*2+1,mid+1,r,mid+1,rr));
}
}
int t=0;
void dfs(int u){
tin[u]=++t;
for(auto v:V[u]){
if(tin[v])continue;
dfs(v);
}
tout[u]=t;
}
int M[MAX_N];
int main(){
//freopen("1.in","r",stdin);
//ios::sync_with_stdio(false);
// freopen("1.in","r",stdin);
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i]);
M[arr[i]]=i;
}
for(int i=1;i<=n;i++)par[i]=i;
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
e[i]=pii(x,y);
}
for(int i=1;i<=q;i++){
scanf("%d%d",&x,&y);
que[i]=pii(x,y);
if(x==2){
del[y]=1;
}
}
for(int i=1;i<=m;i++){
if(!del[i])unite(e[i].first,e[i].second);
}
for(int i=q;i>=1;i--){
if(que[i].first==2){
int v=que[i].second;
unite(e[v].first,e[v].second);
}
else{
que[i].second=find_p(que[i].second);
}
}
//cout<<"***"<<endl;
for(int i=1;i<=n;i++){
if(find_p(i)==i){
dfs(i);
}
}
for(int i=1;i<=n;i++){
update(1,1,t,tin[i],arr[i]);
}
for(int i=1;i<=q;i++){
int op=que[i].first;
if(op==1){
int v=que[i].second;
int x=query(1,1,t,tin[v],tout[v]);
printf("%d\n",x);
if(x!=0)update(1,1,t,tin[M[x]],0);
}
}
}
------
author: num73