一道平面图转对偶图+启发式分裂的练习题

题意

【题⽬描述】
给你⼀个连通的平⾯图(平⾯图是可以画在平⾯上并且使得不同的边可以互不交叠的图)。接下来有 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 n105,q2×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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值