Luogu5021 [NOIP2018]賽道修建
阿新 • • 發佈:2018-11-25
Description
給你一棵樹,樹上每條邊都有一個邊權。你要在上面選出\(m\)條沒有重複邊的路徑,使得選出的最短路徑儘量的長
Solution
最短的最長,這顯然就是二分答案
然後就直接在樹上貪心就可以了,對於每一個點把它的字樹儘可能多的兩兩匹配,最後如果有匹配不了的就與當前點連向父親的邊連起來,這個匹配可以用\(set\)去實現
Code
#include <bits/stdc++.h> using namespace std; #define fst first #define snd second #define squ(x) ((LL)(x) * (x)) #define debug(...) fprintf(stderr, __VA_ARGS__) typedef long long LL; typedef pair<int, int> pii; inline int read() { int sum = 0, fg = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1; for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30); return fg * sum; } typedef set<pii>::iterator It; const int maxn = 5e4 + 10; int n, m, cnt, ans, Begin[maxn], Next[maxn << 1], To[maxn << 1], w[maxn << 1], e ; void Add(int x, int y, int z) { To[++e] = y, Next[e] = Begin[x], Begin[x] = e, w[e] = z; } int S[maxn], len[maxn]; bool v[maxn]; set<pii> Set; void dfs(int now, int f) { for (int i = Begin[now]; i + 1; i = Next[i]) { int son = To[i]; if (son == f) continue; dfs(son, now); } Set.clear(), S[0] = 0; for (int i = Begin[now]; i + 1; i = Next[i]) { int son = To[i]; if (son == f) continue; S[++S[0]] = len[son] + w[i]; } sort(S + 1, S + S[0] + 1); It lst = Set.begin(); for (int i = 1; i <= S[0]; i++) { if (S[i] >= ans) { ++cnt, v[i] = 1; continue; } lst = Set.insert(lst, (pii){S[i], i}), v[i] = 0; } for (int i = 1; i <= S[0]; i++) { if (v[i]) continue; It pos = Set.lower_bound((pii){ans - S[i], 0}); if (pos == Set.end() || (*pos) == (pii){S[i], i}) continue; v[i] = 1, v[pos->snd] = 1, ++cnt; Set.erase((pii){S[i], i}), Set.erase(pos); } len[now] = 0; for (int i = S[0]; i >= 1; i--) if (!v[i]) { len[now] = S[i]; break; } } bool check(int mid) { ans = mid, cnt = 0; dfs(1, 0); return cnt >= m; } int main() { freopen("track.in", "r", stdin); freopen("track.out", "w", stdout); e = -1, memset(Begin, -1, sizeof Begin); n = read(), m = read(); for (int i = 1; i < n; i++) { int x = read(), y = read(), z = read(); Add(x, y, z), Add(y, x, z); } int l = 1, r = 5e8; while (l <= r) { int mid = (l + r) >> 1; if (check(mid)) l = mid + 1; else r = mid - 1; } cout << r << endl; return 0; }
Summary
這道題一定要注意\(set\)的使用方法 考場上寫掛了
要先用一個數組把\(set\)裡的元素存起來,然後去遍歷這個陣列。在\(set\)中刪除一個元素的時候,直接對這個元素的編號打一個\(vis\)標記就可以了