2020暑假牛客多校10 C -Decrement on the Tree (邊權轉點權處理)
阿新 • • 發佈:2020-08-14
C Decrement on the Tree
題目:
一棵樹每次選擇一條路徑將路徑上的邊權都減1,問最少多少次操作後所有邊權變成0。
題解:
看了好幾篇部落格才明白了,這道題的做法是將對邊權的處理轉變成去想對點的處理,算各點的貢獻。之所以可以這樣做是基於給一條邊的邊權-1,相當於訪問這條邊所連2個點各1次。
如果不轉換思路去考慮各邊對結果的貢獻,邊的邊權知道,兩點知道,其他的如選的路徑每次是什麼都不清楚,所以會有一種思路很”窄“的感覺。但是換作考慮點的貢獻,對每一個點,它連著好幾條邊。任意兩個邊都可以形成路徑,將所有邊減為0相當於這個點所連的邊考慮邊的權值關係下相互匹配,至於什麼關係下面討論,反正思路是很開闊的。這應該算是一種處理圖問題的重要思路,當然有時候也把點的權值問題轉為邊的問題。
討論一個點各邊權值關係的處理:
(1) 最好想情況是 一個邊權超級大,那麼其他邊都和這一邊組成路徑把其他邊消為0,最後肯定這條邊無法”蹭“別的路徑去消除,只能自己消除,這就是當前點的貢獻。設當前點為u
, 其所連的總邊權為sum
, 超級大邊權為maxx
。得該點貢獻為:(sum-maxx是除maxx外其它邊權和)
\[maxx - (sum - maxx) = 2*maxx-sum \]
容易發現maxx >= sum / 2 時就屬於情況(1) 。 感性理解就是其他邊權和加起來都不能消除完maxx時是情況(1)
(2) 如果maxx < sum / 2, 且u相連邊總邊權sum
為偶數時,肯定可以相互匹配完。貢獻為0. sum
這樣遍歷一遍每個結點就可以求出中貢獻,但是一個邊對應2個點,所以算的結果應該除2才是對邊得最少運算元。
至於q次詢問,每次O(1) 修改一下p邊所連點得資訊,重新算一下該邊相連兩點得貢獻就可以算出答案。
程式碼:
#include<bits/stdc++.h> using namespace std; #define rep(i, a, n) for(int i = a; i <= n; ++ i); #define per(i, a, n) for(int i = n; i >= a; -- i); typedef long long ll; const int N = 2e6+ 5; const int mod = 998244353; const double Pi = acos(- 1.0); const int INF = 0x3f3f3f3f; const int G = 3, Gi = 332748118; ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; } ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; } ll lcm(ll a, ll b) { return a * b / gcd(a, b);} bool cmp(int a, int b){ return a > b;} // int n, q; ll res; multiset<int> sol[N]; int x[N], y[N]; ll w[N], val[N]; int cal(int i){ //計算每點貢獻 int tp = *sol[i].rbegin(); if(2 * tp >= val[i]) return 2 * tp - val[i]; else return val[i] & 1; } int main() { scanf("%d%d",&n,&q); for(int i = 1; i < n; ++ i){ scanf("%d%d%lld",&x[i],&y[i],&w[i]); val[x[i]] += w[i], val[y[i]] += w[i]; sol[x[i]].insert(w[i]); sol[y[i]].insert(w[i]); } for(int i = 1; i <= n; ++ i) res += cal(i); printf("%lld\n",res / 2); while(q --){ //q次詢問 int p; ll tw; scanf("%d%lld",&p,&tw); res -= cal(x[p]) + cal(y[p]); val[x[p]] -= w[p]; val[y[p]] -= w[p]; sol[x[p]].erase(sol[x[p]].find(w[p])); sol[y[p]].erase(sol[y[p]].find(w[p])); w[p] = tw; val[x[p]] += tw; val[y[p]] += tw; sol[x[p]].insert(tw); sol[y[p]].insert(tw); res += cal(x[p]) + cal(y[p]); printf("%lld\n",res / 2); } return 0; }