codeforces 141C Clearing Up
题意:
给出n个点,m条边,边有两种,求一棵生成树,使得这棵树中的两种边数量相等。
题解:
不妨令第一种边的边权为-1,第二种边权为1,首先求一个最小生成树,尽量使用-1的边,然后对所有使用的边进行标记,然后初始化并查集,然后将所有用过的边权为1的边加入并查集,然后加入边权为1的边,使得生成树的边权为(n - 1) / 2,然后加入使用过的边权为-1的边使得生成树的边权为0即可。
代码:
#include <iostream> #include <algorithm> #include <cstdio> using namespace std; const int N = 1e5 + 7; struct edge {int u, v, w, id;} e[N]; bool cmp (edge a, edge b) {return a.w < b.w;} char ch[2]; int n, m, used[N], vis[N], pa[N]; int find (int x) { return x == pa[x] ? x : pa[x] = find (pa[x]); } int main () { scanf ("%d%d", &n, &m); for (int i = 1; i <= m; ++i) { scanf("%d%d%s", &e[i].u, &e[i].v, ch); if (ch[0] == 'S') e[i].w = -1; else e[i].w = 1; e[i].id = i; } if (n % 2 == 0) {puts("-1"); return 0;} sort (e + 1, e + 1 + m, cmp); for (int i = 1; i <= n; ++i) pa[i] = i; int tot = 0, val = 0; for (int i = 1; i <= m; ++i) { int pu = find (e[i].u), pv = find (e[i].v); if (pu != pv) { pa[pu] = pv; ++tot; val += e[i].w; used[e[i].id] = 1; if (tot == n - 1) break; } } if (tot < n - 1) {puts("-1"); return 0;} if (val > 0) {puts("-1"); return 0;} if (val == 0) { printf ("%d\n", n - 1); for (int i = 1; i <= m; ++i) if(used[i]) printf ("%d ", i); return 0; } val = 0; for (int i = 1; i <= n; ++i) pa[i] = i; for (int i = 1; i <= m; ++i) if (e[i].w == 1 && used[e[i].id]) vis[e[i].id] = 1, val++, pa[find(e[i].u)] = find(e[i].v); for (int i = m; i >= 1; --i) { if (val == (n - 1) / 2) break; if (used[e[i].id] || e[i].w == -1) continue; int pu = find (e[i].u), pv = find (e[i].v); if (pu != pv) {val++, pa[pu] = pv, vis[e[i].id] = 1;} } if (val < (n - 1) / 2) {puts("-1"); return 0;} for (int i = 1; i <= m; ++i) { if (val == 0) break; if (!used[e[i].id] || e[i].w == 1) continue; int pu = find (e[i].u), pv = find (e[i].v); if (pu != pv) {val--, pa[pu] = pv, vis[e[i].id] = 1;} } if (val > 0) {puts("-1"); return 0;} printf ("%d\n", n - 1); for (int i = 1; i <= m; ++i) if (vis[i]) printf ("%d ", i); return 0; }
总结:
不知道怎么总结了。。。反正有两种不同的情况组合在一起,就设不同的权,数量相等那么正负相消,生成树嘛,就往那边想就是了QAQ