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
浙公网安备 33010602011771号