【题目描述】:
给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成一个二分图。
【题目解法】:
考虑dfs出一颗生成树
然后我们记录一下哪些返租边构成偶环,哪些构成奇环
如果图中没有奇环,显然每条边都可以删去(自环的情况后面再说)
考虑哪些返祖边可以被删去:
1.图中存在奇环,但仅存在一个,那么这个奇环的返祖边可以被删去
考虑哪些树边可以被删去(下列条件必须两个都满足):
1.是所有奇环的并集
2.不属于任何偶环
证明:1显然,考虑2,如果属于某个偶环,因为他也属于某个奇环,那么删了它两个环并起来还是一个奇环,因此不合法
所以我们处理出每一条树边经过它的偶环和奇环个数
然后先处理一些细节:
1.重边,我们把重边当成偶数环看待就好了
2.自环,显然自环必须被删去,因此:
1.如果图中没有奇环,那么只能删去自环
2.如果图中存在奇环,答案为0
奇环和偶环处理直接维护树上前缀和+lca既可以了
然后就一直WA,发现没有把答案sort一遍(居然在cf上面水过了前30个点TAT)
然后改了PE,发现不能输出行末空格
然后改了接着WA,发现如果答案为0,第二行不能多输出一个换行TAT
然后就A了
复杂度O(N+M)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=1000005;
const int M=2000005;
struct Edge {int x,y,id; bool flag;} B[M];
struct edge {int x,id,next;} b[M];
int n,m,tot,a[N],self,odd,ans,Ans[N],selfid;
int f[N],g[N],dep[N],fa[21][N],cntf,cntg,fid,faid[N];
inline long long getint()
{
long long x=0; char c=getchar(); bool flag=false;
while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
if (c=='-') flag=true,c=getchar();
while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
if (flag) return -x; else return x;
}
inline void addedge(int x,int y,int id)
{
++tot;
b[tot].x=y;
b[tot].id=id;
b[tot].next=a[x];
a[x]=tot;
}
void init()
{
n=getint(); m=getint();
for (int i=1; i<=m; i++)
{
B[i].x=getint(); B[i].y=getint(); B[i].id=i;
if (B[i].x==B[i].y) self++,selfid=i;
}
}
namespace MST
{
int fa[N];
int getfather(int x)
{
if (fa[x]==x) return x;
return fa[x]=getfather(fa[x]);
}
void solve()
{
for (int i=1; i<=n; i++) fa[i]=i;
for (int i=1; i<=m; i++)
{
int x=B[i].x,y=B[i].y; if (x==y) continue;
int fx=getfather(x),fy=getfather(y); if (fx==fy) continue;
fa[fy]=fx; B[i].flag=true; addedge(x,y,i); addedge(y,x,i);
}
}
}
namespace Build
{
bool v[N];
void dfs(int x)
{
v[x]=true;
for (int p=a[x];p;p=b[p].next)
{
int pp=b[p].x; if (pp==fa[0][x]) continue;
fa[0][pp]=x; dep[pp]=dep[x]+1; faid[pp]=b[p].id; dfs(pp);
}
}
inline int LCA(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int k=dep[x]-dep[y],j=0;k;k/=2,j++) if (k&1) x=fa[j][x];
if (x==y) return x;
for (int i=20; i>=0; i--) if (fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
return fa[0][x];
}
inline int dist(int x,int y)
{
int k=LCA(x,y);
return dep[x]+dep[y]-2*dep[k]+1;
}
void DFS(int x)
{
v[x]=true;
for (int p=a[x];p;p=b[p].next)
{
int pp=b[p].x; if (pp==fa[0][x]) continue;
DFS(pp); f[x]+=f[pp]; g[x]+=g[pp];
}
}
void solve()
{
for (int i=1; i<=n; i++) if (!v[i]) {dep[i]=1; dfs(i);}
for (int i=1; i<=20; i++) for (int j=1; j<=n; j++) fa[i][j]=fa[i-1][fa[i-1][j]];
for (int i=1; i<=m; i++)
{
int x=B[i].x,y=B[i].y; if (x==y) continue;
if (B[i].flag) continue; int k=LCA(x,y),dis=dist(x,y);
if (dis%2==0) cntg++,g[x]++,g[y]++,g[k]-=2;
if (dis%2==1) cntf++,f[x]++,f[y]++,f[k]-=2,fid=i;
}
memset(v,false,sizeof(v));
for (int i=1; i<=n; i++) if (!v[i]) DFS(i);
}
}
inline bool special()
{
if (self!=0)
{
if (self>1) {printf("0\n"); return true;}
if (cntf==0) {printf("1\n"); printf("%d\n",selfid); return true;}
printf("0\n"); return true;
}
if (cntf==0)
{
printf("%d\n",m);
for (int i=1; i<=m-1; i++) printf("%d ",i); printf("%d\n",m);
return true;
}
return false;
}
namespace NoneTree
{
void solve()
{
if (cntf>1) return;
Ans[++ans]=fid;
}
}
namespace Tree
{
bool v[N];
void dfs(int x)
{
if ((faid[x]!=0)&&(f[x]==cntf)&&(g[x]==0)) Ans[++ans]=faid[x]; v[x]=true;
for (int p=a[x];p;p=b[p].next) if (b[p].x!=fa[0][x]) dfs(b[p].x);
}
void solve()
{
for (int i=1; i<=n; i++) if (!v[i]) dfs(i);
printf("%d\n",ans); if (ans==0) return; sort(Ans+1,Ans+ans+1);
for (int i=1; i<=ans-1; i++) printf("%d ",Ans[i]); printf("%d\n",Ans[ans]);
}
}
int main()
{
init();
MST::solve();
Build::solve();
if (special()) return 0;
NoneTree::solve();
Tree::solve();
return 0;
}