题目链接
题目大意:
求由所有根节点到树叶节点带权路径之和,要求经过不断地对某个路径/2,使总和<=给定S,求对路径权重/2的次数。
解题思路:
一条边可能是n个根节点到叶节点的路径的边,此时它对sum的贡献是n*w,我们不能简单根据贡献选取边进行除2,考虑w = 7,n = 7,和w = 10,n =5,此时对sum的cost分别为28和25,因此,我们应该选取对sum cost最大的边进行操作。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define se second
#define fi first
int t,n;
typedef long long ll;
ll sum = 0,S;
const int N = 1e5+5;
vector<pair<int,int> > g[N];
struct pp{
ll w;
ll cnt;
pp(ll _w,ll _cnt):w(_w),cnt(_cnt){
}
bool operator<(const pp& x)const{
ll cost =(w -w / 2) *cnt;
ll x_cost = x.cnt *(x.w - x.w / 2);
return cost <x_cost;
}
};
priority_queue<pp> q;
ll dfs(int x,int pre){
ll s = 0;
if(x != 1 && g[x].size() == 1){
return 1;
}
for(int i = 0;i<g[x].size();i++){
pair<int,int> p = g[x][i];
int v = p.fi,w = p.se;
if(v == pre)
continue;
ll ts = dfs(v,x);
sum += w *ts;
q.push(pp(w,ts));
s += ts;
}
return s;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
sum = 0;
while(!q.empty())q.pop();
scanf("%d%lld",&n,&S);
for(int i = 1;i <= n;i++)
g[i].clear();
for(int i = 1;i <= n-1;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u].pb(mp(v,w));
g[v].pb(mp(u,w));
}
dfs(1,-1);
ll mv = 0;
while(sum>S){
pp x = q.top();
q.pop();
sum -= x.cnt *(x.w - x.w / 2);
q.push(pp(x.w / 2,x.cnt));
mv++;
}
printf("%lld\n",mv);
}
return 0;
}