1. 程式人生 > 實用技巧 >E1 - Weights Division (easy version)

E1 - Weights Division (easy version)

E1 - Weights Division (easy version)

題意:給定一個帶權無環聯通圖(編號為1的節點是這顆樹的根),每一次可以選擇一條邊,將這條邊的權值變成原來的1/2,向下取整。問:當根節點到所有葉子節點的距離的和小於等於題目要求的S時,最少操作次數是?

AC_Code:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 #define endl '\n'
 5 const ll mod=1e9+7;
 6 const int maxn = 1e5+10
; 7 8 vector<ll>w,cnt; //w[i]表示i邊的權值; cnt[i]表示i邊所影響的路徑有幾個 9 vector<vector<pair<ll,ll>>>g; 10 11 ll getdiff(ll i){ //算i邊縮小一半後對整個答案造成的影響 12 return w[i]*1ll*cnt[i]-(w[i]/2)*1ll*cnt[i]; 13 } 14 15 void dfs(ll v, ll fa=-1){ //fa表示v與它的父親連的那條邊的編號
16 if( g[v].size()==1 && fa!=-1 ){ //v是葉子節點 17 cnt[fa]=1; 18 return ; 19 } 20 21 for( auto i: g[v] ){ 22 if( i.second==fa ) continue; 23 dfs(i.first,i.second); 24 if( fa!=-1 ){ 25 cnt[fa]+=cnt[i.second]; 26 } 27 } 28 }
29 30 ll n,s; 31 int main() 32 { 33 int t;cin>>t; 34 while( t-- ){ 35 cin>>n>>s; 36 w = cnt = vector<ll>(n-1); 37 g = vector<vector<pair<ll,ll>>>(n+1); 38 for(int i=0;i<n-1;i++){ 39 ll x,y; cin>>x>>y>>w[i]; 40 g[x].push_back({y,i}); //存連線點和邊的編號 41 g[y].push_back({x,i}); 42 } 43 44 dfs(1); //dfs一遍求出每個邊所能影響的root-->leaves路徑有多少條(也就是每個邊對最後值的貢獻是多少) 45 46 set<pair<ll,ll>>st; //set自動按getdiff的大小從小到大排序 47 ll cur=0; 48 for(int i=0;i<n-1;i++){ //預處理 49 st.insert({getdiff(i),i}); 50 cur+=w[i]*1ll*cnt[i]; 51 } 52 53 ll ans=0; 54 while(cur>s){ 55 int id=st.rbegin()->second; //rbegin:集合裡的最後一個元素 56 st.erase(prev(st.end())); //刪除集合裡的最後一個元素 57 cur-=getdiff(id); 58 w[id]/=2; 59 st.insert({getdiff(id),id}); 60 ans++; 61 } 62 cout<<ans<<endl; 63 } 64 return 0; 65 }