正题
题目链接:https://www.luogu.com.cn/problem/CF19E
题目大意
给出nnn个点mmm条边的一张无向图,求有多少条边去掉后可以使得图变成一张二分图。
1≤n,m≤1041\leq n,m\leq 10^41≤n,m≤104
解题思路
虽然线段树分治可以暴力草过去但是考虑点智慧的做法。
众所周知没有奇环是图是二分图的充要条件,所以答案就是奇环的交。
显然无法考虑所有的奇环,但是我们可以考虑一下简单奇环。
先随便跑出一个生成树,然后枚举所有非树边考虑其在树上的环,只考虑树边的话答案肯定是所有简单奇环的交。并且这条边不能出现在一个枚举出的偶环上,因为如果这条边即在一个奇环又在一个偶环上,那么这两个环的不交部分一定是一个奇环,所以这条边就不成立了。
然后对于非树边,显然条件就是这条边必须得是唯一一个树上奇环的边。
用dfsdfsdfs搜出生成树,此时所有的非树边都是返祖边,这样我们就可以用树上差分计算树边的条件了。
时间复杂度O(n+m)O(n+m)O(n+m)(不算答案用的排序)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e4+10;
struct node{int to,next,w;
}a[N<<1];
int n,m,tot,cnt,ant,last;
int ls[N],v[N],s[N],from[N],ans[N];
void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];a[tot].w=w;ls[x]=tot;return;
}
void dfs(int x){for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if((i^1)==from[x])continue;if(v[y]&&v[x]>v[y]){if((v[x]-v[y])&1)s[x]--,s[y]++;else s[x]++,s[y]--,cnt++,last=a[i].w;}else if(!v[y]){v[y]=v[x]+1;from[y]=i;dfs(y);s[x]+=s[y];}}return;
}
int main()
{scanf("%d%d",&n,&m);tot=1;for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y);addl(x,y,i);addl(y,x,i);}for(int i=1;i<=n;i++)if(!v[i])v[i]=1,dfs(i);if(!cnt){printf("%d\n",m);for(int i=1;i<=m;i++)printf("%d ",i);return 0;}if(cnt==1)ans[++ant]=last;for(int i=1;i<=n;i++)if(s[i]==cnt)ans[++ant]=a[from[i]].w;sort(ans+1,ans+1+ant);ant=unique(ans+1,ans+1+ant)-ans-1;printf("%d\n",ant);for(int i=1;i<=ant;i++)printf("%d ",ans[i]);return 0;
}