题解 CF19E Fairy【二分图】

题目链接:Codeforces

题目大意

给一个无向图,要求删去一条边,让它变为二分图。问哪些边符合要求?

题解

(这题考试时拿来加强了一波,要求线性。结果我分部分分写,暴力挂了正解对了)

首先二分图的充要条件是没有奇环,着重考虑有奇环的情况(没有奇环则所有边都可行。):

显然这条边必须在同时所有奇环上,于是我们先找到一个奇环,记它为环 A A A。BFS+染色就能实现。把环上的点和边记录下来。

接着考虑剩下的奇环:

  • 若这个奇环没有边在 A A A 上,则没有边符合要求。
  • 若这个奇环有一些边在 A A A 上,则这些边以外的边都不可行

所以将环上所有边去掉(打个标记),简单判断第一种情况,然后依次从环上的点开始 BFS,寻找环上的点到另一个点的各个路径,每个路径都可以与 A A A 的连续一段构成奇环。如下图, 深蓝色 \color{#0080FF}\text{深蓝色} 深蓝色的环即环 A A A 浅蓝色 \color{#66CCFF}\text{浅蓝色} 浅蓝色(及 绿 色 \color{#66FFFF}绿色 绿)的边与环上用对应颜色虚线标明的部分构成奇环。用 红色 \color{#EE0000}\text{红色} 红色圈出的边即符合要求的边。
题解 CF19E Fairy【二分图】_第1张图片
考虑怎么维护环上哪些边符合要求。记录序列 a a a(每个元素对应 A A A 的一条边)与变量 c n t = 0 cnt=0 cnt=0。每次找到有边在 A A A 上的环,便将 c n t cnt cnt 加一, A A A 上的这一段边在 a a a 上对应的值也加一。由于这一段边一定是连续的(把环拆开成链后至多分成两段),可以用差分数组维护。最后 O ( N ) O(N) O(N) 扫一遍, a i = c n t a_i=cnt ai=cnt 者即符合要求。

时空复杂度都为 O ( N + M + k log ⁡ k ) O(N+M+k\log k) O(N+M+klogk) k k k 为答案数量)

using namespace std;
int getint(){
	int ans=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		ans=ans*10+c-'0';
		c=getchar();
	}
	return ans*f;
}
const int N=2000010,M=N<<1;
struct bian{
	int e,n;
	bool on;
};
bian b[M];
int s[N],tot=1;
void add(int x,int y){
	tot++;
	b[tot].e=y;
	b[tot].n=s[x];
	s[x]=tot;
}
int n,m;

int col[N],dis[N];//col: 染色; dis: 离 BFS 起点的距离
int pre[N],preb[N];//pre: BFS 时的前驱; preb: BFS 时的前一条边
int huan[N],num[N];//huan: 环上第 i 个点; num: 点在环上的编号
int huanb[N];//haunb: 环上的第 i 条边
int a[N];//a: 题解中的 a 的差分数组
int len,cnt=0;

void record(int x,int y,int xtoy){//记录奇环
	int x_=x,y_=y;
	while(dis[y]>dis[x]){
		b[preb[y]].on=b[preb[y]^1].on=1;
		y=pre[y];
	}
	while(x!=y){
		b[preb[y]].on=b[preb[y]^1].on=1;
		y=pre[y];
		b[preb[x]].on=b[preb[x]^1].on=1;
		x=pre[x];
	}
	int lca=x;
	x=x_,y=y_;
	len=dis[x]+dis[y]-dis[lca]*2+1;
	while(x!=lca){
		huan[dis[x_]-dis[x]+1]=x;
		num[x]=dis[x_]-dis[x]+1;
		huanb[num[x]]=preb[x]>>1;
		x=pre[x];
	}
	huan[dis[x_]-dis[x]+1]=x;
	num[x]=dis[x_]-dis[x]+1;
	while(y!=lca){
		huan[len-dis[y_]+dis[y]]=y;
		num[y]=len-dis[y_]+dis[y];
		huanb[num[y]-1]=preb[y]>>1;
		y=pre[y];
	}
	huanb[len]=xtoy>>1;
	b[xtoy^1].on=b[xtoy].on=1;
}
bool find_jihuan(int st){//找奇环,返回是否找到
	queueq;
	q.push(st);
	col[st]=1;
	while(q.size()){
		int f=q.front();
		q.pop();
		for(int i=s[f];i;i=b[i].n){
			if(col[b[i].e]==col[f]){
				record(f,b[i].e,i);
				return 1;
			}
			if(!col[b[i].e]){
				col[b[i].e]=3-col[f];
				pre[b[i].e]=f;
				preb[b[i].e]=i;
				dis[b[i].e]=dis[f]+1;
				q.push(b[i].e);
			}
		}
	}
	return 0;
}
bool bfs(int x){//从环上的点开始 BFS,返回是否**没有**其他奇环
	queueq;
	q.push(x);
	col[x]=1;
	while(q.size()){
		int f=q.front();
		q.pop();
		for(int i=s[f];i;i=b[i].n){
			if(b[i].on)continue;
			if(col[b[i].e]==col[f]){
				return 0;
			}
			if(!col[b[i].e]){
				col[b[i].e]=3-col[f];
				q.push(b[i].e);
				if(num[b[i].e]){
					cnt++;
					if((col[x]+col[b[i].e]+num[x]+num[b[i].e])%2==1){
						int u=x,v=b[i].e;
						if(num[u]>num[v])swap(u,v);
						a[num[u]]++;
						a[num[v]]--;
					}else{
						int u=x,v=b[i].e;
						if(num[u]>num[v])swap(u,v);
						a[1]++;
						a[num[u]]--;
						a[num[v]]++;
						a[len+1]--;
					}
				}
			}
		}
	}
	return 1;
}


int main(){
	n=getint(),m=getint();
	for(int i=0;ians;
	for(int i=1;i<=len;i++){
		sum+=a[i];
		if(sum==cnt)ans.push_back(huanb[i]);
	}
	sort(ans.begin(),ans.end());
	printf("%d\n",ans.size());
	for(int i=0;i

你可能感兴趣的:(题解,#,来源-codeforces,#,图论-二分图)