题意:给定你一个根为1号节点的有根树,然后n-1条边每条边都有一个权值w,然后给定你一个操作,每次操作可以把某一条边的边权值变为w/2,向下取整,然后问你最少经过过少次操作可以使得这棵树的所有边权和加起来小于等于S。
思路:假如不考虑一棵树的情况,我们直接可以把每条边的边权值存起来,然后从大到小的选边进行操作等到全部的和小于等于S即可。但对于一棵树来说,选择的走到叶子节点的路径权值和,某个节点向下的边是可以对他所有的子节点的子树路径和产生影响的。
所以我们应该考虑的是让边权值改变是带来影响更大的边我们先更新,所有就可以dfs出每个边连接的节点的子树中,有几条路径能走到叶子节点,也就是有几个叶子节点,用他们我的边权值乘上这个叶子节点个数才是他们能真正做出的影响。
void dfs(int u,int fa = -1)//用u来代表节点 而fa代表他与父节点连接的那条边的序号
{
if(fa != -1 && G[u].size() == 1){//代表到达了叶子节点
cnt[fa] = 1;//储存的下标信息也是边的序号的
return ;
}
for(auto i: G[u]){
int v = i.second;
if(v == fa) continue;
dfs(i.first,v);
if(fa != -1)
cnt[fa] += cnt[v];
}
}
再用一个set<pair<int,int>>s,因为set存储pair时是会自动对pair的第一关键字进行排序,所以只需要把二元组cnt[i]*w[i]和i这个编号存入即可。
再另外补充一个prev库函数,会自动取得当前迭代器位置前一个位置的元素,而end()又是指向最后一个元素后面一个元素的位置,所以即可用prev(s.end())来获得集合的最后一个位置。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+7;
int n;
//贡献还要看根的子节点的子树中能到达的叶子节点的数目 也就是他能所影响到的路径的数目
ll sum;
//用set完成了一个自动的排序
vector<ll>w,cnt;
vector<vector<pair<ll,ll>>>G;//用一个二维的vector存值 第一位存权值 第二位存边的序号数
ll get_val(ll i)//进行剪枝的时候不能单纯看权值大小,还要看和他相连的叶子节点的个数,也就是他会影响到的路径数
{
return w[i] * cnt[i] - w[i]/2 * cnt[i];
}
void dfs(ll u,ll fa = -1)//fa 表示它与父节点相连的那条边的编号
{
if(fa != -1 && G[u].size() == 1){//走到了叶子节点
cnt[fa] = 1;
return ;
}
for(auto i: G[u]){
int v = i.second;
if(v == fa) continue;
dfs(i.first,v);
if(fa != -1) cnt[fa] += cnt[i.second];//不是第一条边的情况的话 再加边
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%lld",&n,&sum);
w = cnt = vector<ll>(n);//存下对应编号下的边权值
G = vector<vector<pair<ll,ll>>>(n+1);//开一个对应大小的vector数组
for(int i = 1;i < n;i ++){
ll u,v;
scanf("%lld%lld%lld",&u,&v,&w[i]);
G[u].push_back({v,i});
G[v].push_back({u,i});
}
dfs(1);
set<pair<ll,ll>>s;
ll cur = 0;
for(int i = 1;i < n;i ++){
s.insert({get_val((ll)i),(ll)i});
cur += w[i]*cnt[i];
}
ll ans = 0;
while(cur > sum){
int id = s.rbegin()->second;//直接找到集合最后的一个元素
s.erase(prev(s.end()));//prev返回迭代器指向的上一个元素 s.end()指向最后一个元素之后的指针 所以prev(end)就是集合中最后一个元素
cur -= get_val(id);
w[id] /= 2;
s.insert({get_val(id),id});
ans++;
}
printf("%lld\n",ans);
}
return 0;
}