【BZOJ4424】Cf19E Fairy DFS树

【BZOJ4424】Cf19E Fairy

Description

给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成一个二分图。

Input

第 1 行包含两个整数 n,m。分别表示点数和边数。
第 2 到 m+1 行每行两个数 x,y 表示有一条(x,y)的边。

Output

输出第一行一个整数,表示能删除的边的个数。
接下来一行按照从小到大的顺序输出边的序号。

Sample Input

4 4
1 2
1 3
2 4
3 4

Sample Output

4
1 2 3 4

HINT

100%的数据,n,m<=1000000

题解:先建出DFS树,然后每条非树边都对应一个简单环。找出所有奇环偶环及其覆盖的树边,然后分类讨论:

如果没有奇环,那么删哪条边都行。
如果只有一个奇环,那么可以删它覆盖的树边,也可以删这条非树边。
如果有多个奇环,那么必须删掉被所有奇环都覆盖的边。

但是问题来了,奇环+偶环=奇环,也就意味着如果一条边即被奇环覆盖也被偶环覆盖,那么删掉这条边是没有的,判掉就好。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=1000010;
int n,m,sum,ans,cnt;
int f[maxn],to[maxn<<1],next[maxn<<1],ont[maxn<<1],head[maxn],pa[maxn],pb[maxn],vis[maxn],dep[maxn];
int fa[20][maxn],Log[maxn],pc[maxn],s1[maxn],s0[maxn],from[maxn],ok[maxn];
int find(int x)
{
	return (f[x]==x)?x:(f[x]=find(f[x]));
}
inline void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void dfs1(int x)
{
	vis[x]=1;
	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])
		ont[i]=1,fa[0][to[i]]=x,dep[to[i]]=dep[x]+1,from[to[i]]=(i>>1)+1,dfs1(to[i]);
}
inline int lca(int a,int b)
{
	if(dep[a]<dep[b])	swap(a,b);
	for(int i=Log[dep[a]-dep[b]];i>=0;i--)	if(dep[fa[i][a]]>=dep[b])	a=fa[i][a];
	if(a==b)	return a;
	for(int i=Log[dep[a]];i>=0;i--)	if(fa[i][a]!=fa[i][b])	a=fa[i][a],b=fa[i][b];
	return fa[0][a];
}
void dfs2(int x)
{
	for(int i=head[x];i!=-1;i=next[i])	if(ont[i])	dfs2(to[i]),s1[x]+=s1[to[i]],s0[x]+=s0[to[i]];
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	int i,j;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++)	f[i]=i;
	for(i=1;i<=m;i++)
	{
		pa[i]=rd(),pb[i]=rd(),add(pa[i],pb[i]),add(pb[i],pa[i]);
		if(find(pa[i])!=find(pb[i]))	f[f[pa[i]]]=f[pb[i]];
	}
	for(i=1;i<=n;i++)	if(!vis[i])	dep[i]=1,dfs1(i);
	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
	for(j=1;(1<<j)<=n;j++)	for(i=1;i<=n;i++)	fa[j][i]=fa[j-1][fa[j-1][i]];
	for(i=1;i<=m;i++)	if(!ont[i*2-2]&&!ont[i*2-1])
	{
		pc[i]=lca(pa[i],pb[i]);
		if(!((dep[pa[i]]^dep[pb[i]])&1))	sum++,s1[pa[i]]++,s1[pb[i]]++,s1[pc[i]]-=2;
		else	s0[pa[i]]++,s0[pb[i]]++,s0[pc[i]]-=2;
	}
	for(i=1;i<=n;i++)	if(dep[i]==1)	dfs2(i);
	if(!sum)
	{
		printf("%d\n",m);
		for(i=1;i<=m;i++)	printf("%d%c",i,i==m?‘\n‘:‘ ‘);
		return 0;
	}
	if(sum==1)	for(i=1;i<=m;i++)	if(!ont[i*2-2]&&!ont[i*2-1]&&!((dep[pa[i]]^dep[pb[i]])&1))	ok[i]=1,ans++;
	for(i=1;i<=n;i++)	if(s1[i]==sum&&!s0[i])	ok[from[i]]=1,ans++;
	printf("%d\n",ans);
	for(i=1;i<=m;i++)	if(ok[i])	printf("%d%c",i,(--ans)?‘ ‘:‘\n‘);
	return 0;
}//5 6 1 2 1 3 2 3 2 5 3 6 5 6
时间: 2024-10-13 06:28:51

【BZOJ4424】Cf19E Fairy DFS树的相关文章

bzoj千题计划229:bzoj4424: Cf19E Fairy

http://www.lydsy.com/JudgeOnline/problem.php?id=4424 图是二分图的条件:没有奇环 所以,如果图不存在奇环,删除任意一条边都可以 如果存在奇环, 对于树边来说: 那么可能可以删除的边一定在所有奇环的交集内 而且这条边不能在偶环内 因为如果一条边既是奇环上的一条边,又是偶环上的一条边 删除这条边后,这个奇环和偶环会合并成一个新的奇环 所以最终的答案= 奇环的交集-偶环的并集 对于非树边来说: 如果只有一个奇环,那么可以删除构成环的这条非树边 树边和

BZOJ 4541: [Hnoi2016]矿区 平面图转对偶图+DFS树

4541: [Hnoi2016]矿区 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 433  Solved: 182[Submit][Status][Discuss] Description 平面上的矿区划分成了若干个开发区域.简单地说,你可以将矿区看成一张连通的平面图,平面图划分为了若 干平面块,每个平面块即为一个开发区域,平面块之间的边界必定由若干整点(坐标值为整数的点)和连接这些整点 的线段组成.每个开发区域的矿量与该开发区域的面积有关:具

【BZOJ4238】电压 DFS树

[BZOJ4238]电压 Description 你知道Just Odd Inventions社吗?这个公司的业务是“只不过是奇妙的发明(Just Odd Inventions)”.这里简称为JOI社. JOI社的某个实验室中有着复杂的电路.电路由n个节点和m根细长的电阻组成.节点被标号为1~N 每个节点有一个可设定的状态[高电压]或者[低电压].每个电阻连接两个节点,只有一端是高电压,另一端是低电压的电阻才会有电流流过.两端都是高电压或者低电压的电阻不会有电流流过. 某天,JOI社为了维护电路

【bzoj4016】[FJOI2014]最短路径树问题 堆优化Dijkstra+DFS树+树的点分治

题目描述 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小.注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小).到达该点后按原路返回,然后往其他点走,直到所有点都走过. 可以知道,经过的边会构成一棵最短路径树.请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长

zstu.4191: 无向图找环(dfs树 + 邻接表)

4191: 无向图找环 Time Limit: 5 Sec  Memory Limit: 128 MB Submit: 117  Solved: 34 Description 给你一副无向图,每条边有边权,保证图联通,现在让你判断这个图是否有异或值大于零的环存在. Input 多组测试数据,每组先输入两个数n m,表示图的点跟边的数量. 然后是m行,每行三个数a b c.代表一条边的起点,终点,边权. 1 <= n<= 100000, 1 <= m <= 200000. 1 <

HDOJ 4582 - DFS spanning tree - DFS树,贪心

题目大意: 给定一个N个点.M条边的无向图Graph,以及从点1开始进行DFS形成的树Tree,定义"T-Simple Circle"为Graph中的环,要求其中只含一条不属于Tree的边. 将Graph中的一些边进行染色,使得其中每个T-simple Circle都至少包含一条被染色的边,求最少需要染色的边数. N≤2e3,M≤2e4 本题关键的一点在于Tree是一棵DFS生成树,这样Tree以外的边只可能将某个点与它在Tree中的祖先相连(用反证法可以证明,只有这样才能维持DFS树

[CF19E]Fairy

题意:给一个图,问删除哪些边可以让原图变为二分图 继续旧题补档,这题当时打比赛加强到了$2000000$ 一个图是二分图当且仅当它不含奇环,所以我们要做的就是删边以破坏奇环 对图dfs,得到dfs树,此时非树边只会是返祖边,返祖边+树上路径可以构成环,我们把这种环称为“简单环” 如果没有简单奇环,那么它本来就是二分图,删掉任意一条边还是二分图 如果只有$1$个简单奇环,那么构成这个简单奇环的返祖边是可以删的 一条树边能删当且仅当所有奇环经过它而且没有偶环经过它,因为既要破坏所有奇环又不能构成新的

BZOJ5203 [NEERC2017 Northern] Grand Test 【dfs树】【构造】

题目分析: 首先观察可知这是一个无向图,那么我们构建出它的dfs树.由于无向图的性质我们可以知道它的dfs树只有返祖边.考虑下面这样一个结论. 结论:若一个点的子树中(包含自己)有两个点有到它祖先的返祖边(不包括到它自己), 首先我们证明S和T肯定在DFS树中是祖先关系,接着证明到T至少有一条返祖边,那么这个结论就是显然的了. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 102000; 5

DFS 树

声明 本文部分内容来自 Codeforces 上的一篇博客,侵删. DFS 是一种常见的图遍历方法. 考虑 无向图 的遍历过程:我们访问一个节点,遍历它的所有相邻节点,如果没有访问则去访问.不难发现每个节点只会被访问一次,也即这些节点和所有访问到的边可以构成一棵树,我们称这棵树为 DFS 树.访问过的边称为生成边(span edge),没有访问的称为后向边(back edge). 仔细观察后向边,我们可以发现一些性质: 每条后向边只会连接祖先和子孙,不会有兄弟相连. 这个很好证明,如果有一条边连