题意
给定一个无向图,求一个边集\(E\),删掉这个边集中的任意一条边,都可以使原无向图成为一个二分图
输出这个边集的大小以及这个边集中所有边的编号的异或和
解法
首先丢出两个结论:
二分图中不存在奇环(这应该是众所周知的吧)
构建出一个无向图的生成树,非树边一定是返祖边
证明:设边\((u,v),depth(u)≤depth(v)\),假如\(u\)不是\(v\)的祖先,那么\(v\)必然会\(dfs\)到\(u\)使得\(u\)是\(v\)的儿子,便与\(depth(u)≤depth(v)\)矛盾
如果删掉一条边后形成的图是二分图,那么它一定不存在任意一个奇环
那么我们删掉的边一定是该图中所有奇环的并
删掉这条边就相当于摧毁了所有奇环
所以如果有两个或以上不相交的奇环,那么一定无解
但是我们发现如果仅仅这样进行判断还不够
如果有一条边是一个奇环和一个偶环的并,那么删掉这条边后原来的奇环与偶环会相连形成一个大奇环
这样的边显然也是不合要求的
那么现在考虑怎么做
首先\(dfs\)造出一颗生成树,我们能发现,每个环一定会包含一个返祖边
那么我们对每个返祖边进行答案的计算
用树上差分标记每条边被某个奇环\(/\)偶环覆盖了几次,并顺便求出奇环的个数\(cnt\)
统计答案时,如果一条边被覆盖了\(cnt\)次并且没有被任意一个偶环覆盖,那么它一定是答案
上面这样做的前提是我们的奇环个数\(>1\)
因为如果奇环个数大于\(1\),返祖边便不可能是答案
但如果我们只有一个奇环,返祖边理所当然的也应该是答案的一部分
所以我们标记一个返祖边,\(cnt=1\)的时候分类讨论即可
代码
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e6 + 10;
int n, m, cnt, spe;
int vis[N], fa[N], up[N], dep[N];
int f[N], g[N];
vector<int> vec[N], id[N];
inline void add(int x, int y, int ID) {
vec[x].push_back(y), vec[y].push_back(x);
id[x].push_back(ID), id[y].push_back(ID);
}
void DFS(int x) {
// printf("dfn: %d\n", x);
vis[x] = 1, dep[x] = dep[fa[x]] + 1;
int sz = vec[x].size();
for (int i = 0; i < sz; ++i) {
int to = vec[x][i];
if (vis[to]) continue;
fa[to] = x, up[to] = id[x][i];
DFS(to);
}
}
void DFS2(int x) {
// printf("dfn: %d\n", x);
int sz = vec[x].size();
for (int i = 0; i < sz; ++i) {
int to = vec[x][i];
if (fa[x] == to) continue;
if (fa[to] == x)
DFS2(to);
else if (dep[to] < dep[x]) {
if ((dep[x] - dep[to]) & 1) {
g[x]++, g[to]--;
// printf("even ring: %d %d\n", x, to);
} else {
++cnt, spe = id[x][i];
f[x]++, f[to]--;
// printf("odd ring: %d %d\n", x, to);
}
}
}
}
void DFS3(int x) {
// printf("dfn: %d\n", x);
// printf("f[%d]: %d \n", x, f[x]);
int sz = vec[x].size();
for (int i = 0; i < sz; ++i) {
int to = vec[x][i];
if (fa[to] == x) {
DFS3(to);
f[x] += f[to], g[x] += g[to];
}
}
}
void solve() {
// printf("cnt: %d\n", cnt);
int ans = 0, res = 0;
if (!cnt) {
for (int i = 1; i <= m; ++i) res ^= i;
printf("%d\n%d\n", m, res);
}
if (cnt == 1) {
res = spe, ans = 1;
for (int i = 1; i <= n; ++i)
if (f[i] == 1 && !g[i])
res ^= up[i], ++ans;
printf("%d\n%d\n", ans, res);
}
if (cnt > 1) {
for (int i = 1; i <= n; ++i)
if (f[i] == cnt && !g[i])
res ^= up[i], ++ans;
printf("%d\n%d\n", ans, res);
}
}
int main() {
scanf("%d%d", &n, &m);
int x, y;
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &x, &y);
add(x, y, i);
}
for (int i = 1; i <= n; ++i)
if (!vis[i]) DFS(i);
for (int i = 1; i <= n; ++i)
if (!fa[i]) DFS2(i);
for (int i = 1; i <= n; ++i)
if (!fa[i]) DFS3(i);
solve();
return 0;
}