圣战
正解部分
若 x 与 y 之间有敌对关系, 则在 x 和 y 之间连一条边, 依此建出一个图,
当图中存在 奇环 时, 无法完成二染色, 说明无法划分为 2 个合法的阵营,
现在目的就是删去其中一条边, 使得图中不存在 奇环, 自然而然的, 删去的边是所有 奇环 的 交,
所以现在需要求出哪些边是 所有奇环 的 交 .
于是有个最初的做法:
利用 返祖边 配合 树上差分 找出图中的每个点被 奇环 覆盖的次数,
若两个相连的点都被所有奇环覆盖, 说明这两个点之间的边被所有奇环覆盖, 删去这条边 .
但是这样仍然有bug:
当删去某条边时, 若这条边在 偶环 与 奇环 的 交 中, 则删边后关于这条边的 奇环 变 偶环, 偶环 变 奇环,
而新的 奇环 和 新的 偶环 组合会形成新的 奇环, 这就不合法了 .
所以:
当一条边被所有 奇环 覆盖, 且不被任何一个 偶环 覆盖时, 该边删去后 存在合法的方案 将图分为两个不同的阵营 .
实现部分
- 当只存在一个 奇环 时, 返祖边 也可以作为计入答案, 否则 返祖边 不可能计入答案 .
#include<bits/stdc++.h>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 1e6 + 10;
int N;
int M;
int cnt;
int num0;
int Tmp_1;
int Fa[maxn];
int dep[maxn];
int head[maxn];
int Cf[maxn][2];
int Fa_edge[maxn];
bool vis[maxn];
struct Edge{ int nxt, to, w; } edge[maxn << 1];
void Add(int from, int to, int w){
edge[++ num0] = (Edge){ head[from], to, w };
head[from] = num0;
}
void DFS(int k, int fa){
vis[k] = 1, dep[k] = dep[fa] + 1, Fa[k] = fa;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa || vis[to]) continue ;
Fa_edge[to] = edge[i].w; DFS(to, k);
}
}
void DFS_2(int k, int fa){
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
if(Fa[to] == k) DFS_2(to, k);
else if(dep[to] < dep[k] && (dep[to]-dep[k] & 1)) Cf[k][1] ++, Cf[to][1] --;
else if(dep[to] < dep[k]) cnt ++, Cf[k][0] ++, Cf[to][0] --, Tmp_1 = edge[i].w;
}
}
void DFS_3(int k, int fa){
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa || Fa[to] != k) continue ;
DFS_3(to, k); Cf[k][0] += Cf[to][0], Cf[k][1] += Cf[to][1];
}
}
int main(){
N = read(), M = read();
for(reg int i = 1; i <= M; i ++){
int u = read(), v = read();
Add(u, v, i), Add(v, u, i);
}
for(reg int i = 1; i <= N; i ++) if(!vis[i]) DFS(i, 0);
for(reg int i = 1; i <= N; i ++)
if(!Fa[i]) DFS_2(i, 0), DFS_3(i, 0);
int Ans_1 = 0, Ans_2 = 0;
if(!cnt){
Ans_1 = M;
for(reg int i = 1; i <= M; i ++) Ans_2 ^= i;
printf("%d\n%d\n", Ans_1, Ans_2);
return 0;
}
if(cnt == 1) Ans_1 = 1, Ans_2 = Tmp_1;
for(reg int i = 1; i <= N; i ++)
if(Cf[i][0]==cnt && !Cf[i][1]) Ans_1 ++, Ans_2 ^= Fa_edge[i];
printf("%d\n%d\n", Ans_1, Ans_2);
return 0;
}