CF1399E1题解报告

前言

这里使用 vector 存树,别喷。

洛谷题目传送门CF 题目传送门

正文

思路

首选贪心,每次修改都会让总和减一个值,我们只需要每次操作时让这个值最大即可。

不难发现,每次减多少与该边出现过多少次有密切的关系,所以我们可以统计每条边出现多少次,并用结构体存储(等下你就知道为什么了),但是怎么统计呢?
不难发现,\(u\to v\) 出现的次数等于 \(v\) 为根的子树的叶子数量。求叶子数量我们可以地推实现。

对于边权 \(x\),出现次数为 \(v\) 的二元组,一次操作后,可减少 \((x - \lfloor \frac{x}{2} \rfloor) \times v\)

由于每次需要改的边不一样,所以我们需要一个可以自动排序的容器存储,也就是优先队列(或者 set 什么的也可以),然后每次操作直到路径总和 \(\le S\)

记得清空,开 long long!!!

代码

#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N = 1e5 + 1;

struct edge{
  int v, w;
};

int t, n, s, u, v, w, cnt[N], sss; // sss 表示路径总和
vector<edge> g[N];

struct node{
  int sum, c;
  int ff() const{ // 需要多次用到,写个函数
    return (sum - (sum >> 1)) * c;
  }
  bool operator < (const node y) const{
    return ff() < y.ff();
  }
};
priority_queue<node> q;

void dfs(int u, int fa){
  if(g[u].size() <= 1){ // 如果是叶子
    cnt[u] = 1;
  }
  for(auto ed : g[u]){
    if(ed.v != fa){
      dfs(ed.v, u);
      q.push({ed.w, cnt[ed.v]});
      sss += ed.w * cnt[ed.v];
      cnt[u] += cnt[ed.v];
    }
  }
}

void clear(){ // 每次清空
  while(q.size()) q.pop(); // 优先队列没有 clear
  for(int i = 1; i <= n; i++){
    cnt[i] = 0;
    g[i].clear();
  }
  sss = 0;
}

signed main(){
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> t;
  while(t--){
    cin >> n >> s;
    for(int i = 1; i < n; i++){
      cin >> u >> v >> w;
      g[u].push_back({v, w});
      g[v].push_back({u, w}); // vector 存树
    }
    dfs(1, 0);
    int ans = 0;
    while(sss > s){
      node now = q.top();
      q.pop();
      ans++;
      sss -= now.ff();
      now.sum >>= 1;
      q.push(now);
    }
    cout << ans << '\n';
    clear();
  }
  return 0;
}

完结撒花

posted @ 2026-02-02 21:26  tangtianyao0123  阅读(1)  评论(0)    收藏  举报