loj121 动态图连通性 解题报告
loj121 动态图连通性 传送门
题目描述
这是一道被离线爆艹的模板题。
你要维护一张无向简单图。你被要求加入删除一条边及查询两个点是否连通。
0
:加入一条边。保证它不存在。1
:删除一条边。保证它存在。2
:查询两个点是否连通。
输入格式
输入的第一行是两个数 \(N\ M\)。\(N \leq 5000,M \leq 500000\)。
接下来 \(M \)行,每一行三个数 \(\mathit{op}\), \(x\), \(y\)。\(\mathit{op}\) 表示操作编号。
输出格式
对于每一个 \(\mathit{op}=2\) 的询问,输出一行 Y
或 N
,表示两个节点是否连通。
样例 1
输入
200 5
2 123 127
0 123 127
2 123 127
1 127 123
2 123 127
输出
N
Y
N
样例 2
输入
4 10
0 1 2
0 2 3
0 3 1
2 1 4
0 4 3
2 1 4
1 2 3
2 1 4
1 1 3
2 1 4
输出
N
Y
Y
N
数据范围与提示
对于数据点 1,\(N \leq 200,M \leq 200\)。
对于数据点 2,\(N=5,M \leq 30\)。
对于数据点 3,\(N=10,M \leq 1000\),其中查询的次数\( \geq 900\) 次。
对于数据点 4,\(N=300,M \leq 50000\)。
对于数据点 5,\(N=5000,M \leq 200000\),没有操作 1,其中约\( 70 \%\) 是操作 2。
对于数据点 6,\(N=5000,M \leq 200000\),没有操作 1,其中约 \(70 \%\) 是操作 0。
对于数据点 7、8,\(N=100,M \leq 500000\)。
对于数据点 9,\(N=5000,M \leq 500000\),图是一棵树,其直径 \(\leq 6\)。
对于数据点 10, \(N=5000,M \leq 500000\),图是一棵树,其每个点度数 \(\leq 4\)。
P.S. 其实 9 是菊花,10 是单链,而没有放随机树的点...
solution
这个题有点意思。。。。
何止是有点意思啊
连通性可以用并查集维护,但是由于存在删除操作,所以注意不能路径压缩
并查集需要用到启发式合并(就是按秩合并??)
考虑线段树分治。
具体地,假设一条边在时间\(i\)被插入,在时间j被删除,
那么这条边的一个有效时间就是从\(i\)到\(j-1\) ,
我们在线段树的每个节点上开一个vector
,
通过线段树区间赋值把这个操作添加到这些节点中。
之后从左往右扫线段树就行了,并查集更新的时候开一个栈维护一下变化,
出节点时清除该节点的变化
CODE
#include<bits/stdc++.h>
#define fi first
#define se second
#define pai pair<int,int>
using namespace std;
const int N=5e3+2,M=5e5+2;
int n,m,fa[N],sz[N],st[N],top,ans[M],op,x,y;
pai q[M];
map<pai,int> mp;
map<pai,int> ::iterator it;
vector<pai> V[M*4];
void add(int x,int y,int ti){
if(x>y) swap(x,y);
mp[make_pair(x,y)]=ti;
}
void build(int x,int l,int r,int L,int R,pai d){
if(L<=l&&r<=R){
V[x].push_back(d);
return ;
}
int mid=(l+r)>>1;
if(L<=mid) build(x<<1,l,mid,L,R,d);
if(R>mid) build(x<<1|1,mid+1,r,L,R,d);
}
void del(int x,int y,int tm){
if(x>y) swap(x,y);
auto p=make_pair(x,y);
build(1,1,m,mp[p],tm-1,p);
mp[p]=0;
}
int find(int x){
if(x==fa[x]) return x;
return find(fa[x]);
}
void merge(int x,int y){
x=find(x),y=find(y);
if(x==y) return;
if(sz[x]>sz[y]) swap(x,y);
fa[x]=y;
sz[y]+=sz[x];
st[++top]=x;
}
void update(int tp){
while (top>tp){
int x=st[top--];
sz[fa[x]]-=sz[x];
fa[x]=x;
}
}
void solve(int x,int l,int r){
int tp1=top;
for(int i=0;i<(int)V[x].size();i++) {
int xx=V[x][i].fi,yy=V[x][i].se;
merge(xx,yy);
}
if(l==r){
if(q[l].fi) {
int xx=find(q[l].fi),yy=find(q[l].se);
if(xx==yy) ans[l]=1;
else ans[l]=0;
}
update(tp1);
return ;
}
int mid=(l+r)>>1;
solve(x<<1,l,mid);
solve(x<<1|1,mid+1,r);
update(tp1);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
fa[i]=i;sz[i]=1;
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&op,&x,&y);
if(op==0) add(x,y,i);
else if(op==1) del(x,y,i);
else q[i]=make_pair(x,y);
}
for(it=mp.begin();it!=mp.end();it++){
if((*it).se){
auto p=(*it).fi;
del(p.fi,p.se,m+1);
mp[p]=0;
}
}
solve(1,1,m);
for(int i=1;i<=m;i++) {
if(q[i].fi){
if(ans[i]) printf("Y\n");
else printf("N\n");
}
}
return 0;
}
完结撒花❀
★,°:.☆( ̄▽ ̄)/$:.°★ 。