题意
【题⽬描述】
给你⼀个连通的平⾯图(平⾯图是可以画在平⾯上并且使得不同的边可以互不交叠的图)。接下来有
q
q
q次操作:
操作"-",删去边
x
,
y
x,y
x,y,询问删完后有几个连通块。
操作"?",询问
x
,
y
x,y
x,y在不在⼀个连通块中,如果是,输出1,否则输出0。
要求强制在线。
n
≤
1
0
5
,
q
≤
2
×
1
0
5
n\le 10^5,q\le 2\times 10^5
n≤105,q≤2×105
解法:
首先有一个性质:平面图上的一个最小割对应这个平面图转成的对偶图中的一个环。
然后考虑一操作,显然只有当连通性改变时,才会改变答案。所以我们先求出对偶图,然后在对偶图上用并查集维护连通性。
对于二操作,首先给每个点一个标号,表示这个点所在的联通块的编号,然后每次删边,将图的联通性改变时,用启发式分裂去将较小的联通块找出来(会从一个联通块变成两个联通块),并重新编号。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,q,t,fa[maxn],cnt,las,t1;
typedef pair<int,int>pii;
typedef map<int,int>::iterator it;
vector<int> e[maxn];
map<int,int> mp[maxn];
vector<int> d1,d2;
map<pii,int> f,id;
int col[maxn],inq[maxn];
it cur[maxn];
inline int find(int x){return fa[x]==x?fa[x]:fa[x]=find(fa[x]);}
bool push(vector<int> &x,int &p){
for(;p<x.size();p++){
int v=x[p];
for(it k=cur[v];k!=mp[v].end();cur[v]=++k){
int z=k->first;
if(!inq[z]){
inq[z]=1;
x.push_back(z);
cur[z]=mp[z].begin();
return 1;
}
}
}
return 0;
}
int main(){
n=read(),q=read();
for(int i=1;i<=n;i++){
int k=read();
for(int j=1;j<=k;j++){
int x=read();
e[i].push_back(x);//建边
mp[i][x]=1;
id[pii(i,x)]=++t;//每条平面图中的边一个编号(注意这里边有方向)
fa[t]=t;//并查集
}
}
for(int i=1;i<=n;i++){//求出对偶图
int l=e[i].size();
for(int j=1;j<l;j++)fa[find(id[pii(i,e[i][j-1])])]=find(id[pii(e[i][j],i)]);//这里是把某个区域给提出来当成点
fa[find(id[pii(i,e[i].back())])]=find(id[pii(e[i][0],i)]);
}
for(int i=1;i<=n;i++){
int l=e[i].size();
for(int j=0;j<l;j++)f[pii(i,e[i][j])]=find(id[pii(i,e[i][j])]);//f表示边所对应的区域。
}
for(int i=1;i<=t;i++)fa[i]=i;
cnt=1;
while(q--){
char c=getchar();
while((c!='?')&&(c!='-'))c=getchar();
int x=read(),y=read();
x^=las;y^=las;
if(c=='-'){
int u=f[pii(x,y)],v=f[pii(y,x)];
mp[x].erase(y);mp[y].erase(x);//mp表示点所对应的边
if(find(u)==find(v)){
cnt++;
int p1=0,p2=0;
d1.clear(),d1.push_back(x);
d2.clear();d2.push_back(y);
cur[x]=mp[x].begin();cur[y]=mp[y].begin();
while(1){//启发式分裂,每个联通块每次只走一步
if(!push(d1,p1)){
t1++;
for(int i=0;i<d1.size();i++)col[d1[i]]=t1;
break;
}
if(!push(d2,p2)){
t1++;
for(int i=0;i<d2.size();i++)col[d2[i]]=t1;
break;
}
}
for(int i=0;i<d1.size();i++){
inq[d1[i]]=0;
}
for(int i=0;i<d2.size();i++){
inq[d2[i]]=0;
}
}
else{
fa[find(u)]=find(v);
}
printf("%d\n",las=cnt);
}
else printf("%d\n",las=(col[x]==col[y]));
}
return 0;
}