【SHOI2008】堵塞的交通
线段树分治+可撤销并查集
题目大意:
给定一个 $2\times C$ 的网格图,初始没有边。需要支持加边(保证在网格上)、删边、询问两点间连通性。
题解:
这题没有强制在线,所以可以直接离线线段树分治解决。
用线段树分治判断动态图连通性的基本套路。
对时间开线段树,然后离线得到每条边的出现时间,并将其加入线段树上。然后把询问插到线段树对应叶结点中。
处理完后,遍历整棵线段树,到达一个结点时把这些边加上,用并查集来维护。
退出一个结点时,需要撤销相应的加边操作,用栈维护即可。为了保证并查集复杂度的正确性,这里要按秩合并,不能路径压缩,则单次复杂度为非均摊的 $O(\log n)$。
到达叶结点时,直接把询问在并查集上查询即可。
时间复杂度 $O(T\log C\log T)$。
Code:
#include<iostream>
#include<algorithm>
#include<vector>
#define get(x,y)((x-1)*n+y)
using namespace std;
const int N=2e5+5;
int n;
vector<pair<int,int>>vec[N<<2];
pair<int,int>que[N];
int down[N],Right[N],sz[N],fa[N],top;
char s[10],ans[N];
inline int find(int x){while(x!=fa[x])x=fa[x];return x;}
int sta[N*2];
void insert(int l,int r,int o,const int&L,const int&R,const pair<int,int>&v){
if(L<=l&&r<=R)vec[o].push_back(v);else{
const int mid=l+r>>1;
if(L<=mid)insert(l,mid,o<<1,L,R,v);
if(mid<R)insert(mid+1,r,o<<1|1,L,R,v);
}
}
void solve(int l,int r,int o){
const int TOP=top;
for(auto it:vec[o]){
int x=find(it.first),y=find(it.second);
if(x!=y){
if(sz[x]<sz[y])swap(x,y);
sta[++top]=y;
sz[x]+=sz[y],fa[y]=x;
}
}
if(l==r){
if(que[l].first)
ans[l]=(find(que[l].first)==find(que[l].second))?'Y':'N';
}else{
const int mid=l+r>>1;
solve(l,mid,o<<1),solve(mid+1,r,o<<1|1);
}
while(top!=TOP){
int y=sta[top--];
sz[fa[y]]-=sz[y],fa[y]=y;
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
int T=0;
while(cin>>s&&*s!='E'){
++T;
int r1,c1,r2,c2;
cin>>r1>>c1>>r2>>c2;
if(r1>r2)swap(r1,r2),swap(c1,c2);else
if(r1==r2&&c1>c2)swap(c1,c2);
const int id1=get(r1,c1),id2=get(r2,c2);
switch(*s){
case'O':{
if(r1==r2&&!Right[id1])Right[id1]=T;
if(r1<r2&&!down[id1])down[id1]=T;
break;
}
case'C':{
if(r1==r2)insert(1,131072,1,Right[id1],T,make_pair(id1,id2)),Right[id1]=0;
if(r1<r2)insert(1,131072,1,down[id1],T,make_pair(id1,id2)),down[id1]=0;
break;
}
case'A':{
que[T]=make_pair(id1,id2);
break;
}
}
}
for(int i=1;i<=2*n;++i){
if(Right[i])
insert(1,131072,1,Right[i],T,make_pair(i,i+1));
if(down[i])
insert(1,131072,1,down[i],T,make_pair(i,i+n));
}
for(int i=1;i<=2*n;++i)fa[i]=i,sz[i]=1;
solve(1,131072,1);
for(int i=1;i<=T;++i)
if(ans[i])cout.put(ans[i]),cout.put('\n');
return 0;
}