CF 840B Leha and another game about graph

题目描述

https://codeforces.com/problemset/problem/840/B

简要题意:给定一个 $n$ 个 $m$ 条边的无向连通图。现在每个点有一个限制 $d_i$,表示这个点的度数需要为奇数或偶数或者没有限制($d_i=-1$ 表示无限制),问是否能从原图中选出若干条边满足所有限制。

$n,m\le 3\times 10^5$

Solution

注意到选择边无法改变点的度数和的奇偶性,所以如果有限制的点的度数和为奇数则必须将一个 $d_i=-1$ 变成 $d_i=1$,这样做一定有解

因为原图是联通的,所以我们将问题转换成了一个经典问题

有一棵点的颜色为黑色或白色的树,现在有偶数个黑点,每条边选择与否可以翻转连接的两个点的颜色,需要求一个可行的选择方案使所有点都变成白色,注意到只要黑点的个数为偶数则一定有解

所以我们随便搞出一棵生成树就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <vector>
#include <set>
#define maxn 300010
using namespace std;

int n, m, d[maxn];

struct Edge {
int to, next, id;
} e[maxn * 2]; int c1, head[maxn];
inline void add_edge(int u, int v, int id) {
e[c1].to = v; e[c1].id = id;
e[c1].next = head[u]; head[u] = c1++;
}

bool vis[maxn]; vector<int> ans;
bool dfs(int u) {
int res = d[u]; vis[u] = 1;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to, id = e[i].id; if (vis[v]) continue;
if (dfs(v)) res ^= 1, ans.push_back(id);
}
return res;
}

int main() { fill(head, head + maxn, -1);
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m; int sum = 0, p = 0;
for (int i = 1; i <= n; ++i) {
cin >> d[i];
if (~d[i]) sum += d[i];
else if (!p) p = i;
}
if (sum & 1 && !p) return cout << "-1\n", 0;
d[p] = sum & 1 ? 1 : 0;
for (int i = p + 1; i <= n; ++i)
if (d[i] == -1) d[i] = 0;
for (int i = 1; i <= m; ++i) {
int x, y; cin >> x >> y;
add_edge(x, y, i); add_edge(y, x, i);
} dfs(1);
cout << ans.size() << "\n";
for (auto u : ans) cout << u << "\n";
return 0;
}