给出一个连通图,并给每个点赋一个d值0或1或-1,要求选出一个边的集合,使得所有的点i要么d[i] == -1,要么
dgree[i] % 2 == d[i],dgree[i]代表i结点的度数。
考虑一条边都不选的情况,此时所有d[i] == 0的i都满足了题目要求,
此时如果有d[i] == 1的点,我们就要加一条边.
我们考虑用dfs维护这个过程,在dfs序形成的搜索树上,若对于某个节点u,其子节点v有d[v] == 1,那么我们就将u和v之间的边的选取状态取反,即原来选变成不选,不选变成选,然后令d[v] = 0,代表该节点已经满足题意(你愿意赋值成其他值也无所谓,注意别弄混就行),
若d[u] != -1,那么d[u]取反(因为u增加了一度,满足题意的状态肯定变化),可以发现,对于每个点,都能通过改变其和其父节点之间边的选取状态而改变其满足题意的状态,只有根节点没有父节点而例外,
因此dfs完成后,如果根节点的状态为d[root] == 1,那么我们就要再选取一个d[i] == -1的结点i,将其到父节点的路径上所有边的选取状态取反,这样只会改变i和root结点的度数,路径上其他结点的度数并不受影响(因此满足题意的状态也不会改变),而对i来说度数改变是无所谓的,因此这样就只将d[root]变成了0。
那么什么时候输出-1呢,显然当不存在d[i] == -1的i并且d[root] == 1的时候
#include<bits/stdc++.h> #define ll long long #define inf 0x3f3f3f3f using namespace std; typedef pair<int,int> P; const int MAXN = 300010; vector<P> mp[MAXN]; P fa[MAXN]; int d[MAXN]; bool book[MAXN], select[MAXN]; void dfs(int u, int pre) { int v, id; for(int i = 0; i < mp[u].size(); i++) { v = mp[u][i].first; id = mp[u][i].second; if(v == pre || book[v]) continue; book[v] = 1; fa[v] = P(u, id); dfs(v, u); if(d[v] == 1) { d[v] = 0; select[id] ^= 1; if(d[u] != -1) d[u] ^= 1; } } } int main() { int n, m, u, v; int node = 0, ans = 0; cin >> n >> m; for(int i = 1; i <= n; i++) { scanf("%d", d + i); if(d[i] == -1) node = i; } for(int i = 1; i <= m; i++) { scanf("%d %d", &u, &v); mp[u].push_back(P(v, i)); mp[v].push_back(P(u, i)); } dfs(1, -1); if(d[1] == 1) { if(node == 0) { cout << -1; return 0; } while(node != 1) { select[fa[node].second] ^= 1; node = fa[node].first; } } for(int i = 1; i <= m; i++) if(select[i]) ans++; cout << ans << endl; for(int i = 1; i <= m; i++) if(select[i]) printf("%d ", i); return 0; }