2020牛客多校第十場C-Decrement on the Tree
阿新 • • 發佈:2020-09-10
https://ac.nowcoder.com/acm/contest/5675/C
題意
給出一個樹,q次修改,每次修改將第p條邊權值修改為w。
回答q+1詢問,每次操作可以選擇一條鏈,把這條鏈上所有邊的權值減一,問最少進行多少次操作才能把所有邊權全部歸零。
題解
考慮每個節點的邊進行匹配,如果最大邊權值大於了剩下所有邊的權值和,那麼剩下所有邊和它匹配也無法把這條邊的權值匹配完,則會產生\(mx-(sum[u]-mx)=2*mx-sum[u]\)的貢獻,否則,如果邊權和為偶數,則一定能兩兩匹配,也就是子樹中不存在路徑的起訖點,如果為奇數,則存在一個起點,答案+1。
思考到這裡這道題就很簡單了,用一個set維護一下邊權的大小關係,修改時只需要計算相關影響的點對答案的影響即可,注意由於每個點都考慮一遍,所有的邊被考慮了兩遍,ans/=2
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; struct READ { inline char read() { #ifdef _WIN32 return getchar(); #endif static const int IN_LEN = 1 << 18 | 1; static char buf[IN_LEN], *s, *t; return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++; } template <typename _Tp> inline READ & operator >> (_Tp&x) { static char c11, boo; for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) { if(c11 == -1) return *this; boo |= c11 == '-'; } for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0'); boo && (x = -x); return *this; } } in; const int N = 1e5 + 50; struct node { int u, v, w; } edge[N]; multiset<int> st[N]; ll sum[N]; ll ans = 0; int calc(int u) { int mx = *(--st[u].end()); if (2ll * mx >= sum[u]) return 2 * mx - sum[u]; else return sum[u] & 1; } void update(int p, int w) { int u = edge[p].u, v = edge[p].v; ans -= calc(u); ans -= calc(v); sum[u] += w - edge[p].w; sum[v] += w - edge[p].w; st[u].erase(st[u].lower_bound(edge[p].w)); st[u].insert(w); st[v].erase(st[v].lower_bound(edge[p].w)); st[v].insert(w); ans += calc(u); ans += calc(v); edge[p].w = w; } int main() { int n, q; in >> n >> q; for (int i = 1; i < n; i++) { int u, v, w; in >> u >> v >> w; edge[i] = {u, v, w}; sum[u] += w; sum[v] += w; st[u].insert(w); st[v].insert(w); } for (int i = 1; i <= n; i++) ans += calc(i); printf("%lld\n", ans / 2); while (q--) { int p, w; in >> p >> w; update(p, w); printf("%lld\n", ans / 2); } return 0; }