[CF1399E1] Weights Division - 贪心,堆
Description
给定一棵以 1 为根的带权树,一条路径的权值为边的权值之和,你可以进行一系列的操作,操作零次或多次。对于每次操作,你可以选择任意一条边,将其权值除以 2,找到最小操作数,以满足所有从根到叶子的路径的权值之和不超过 S。
Solution
因为要求的是和,所以可以贪心处理
预处理每条边出现次数,所有边扔到堆中,每次取最大的 (w-w/2)c,其中 c 为出现次数,然后再扔回堆中
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000005;
int n, s;
vector<pair<int, int>> g[N];
int w[N], siz[N];
void dfs1(int p, int from)
{
siz[p] = 0;
for (auto [q, wei] : g[p])
if (q != from)
{
w[q] = wei;
dfs1(q, p);
siz[p] += siz[q];
}
siz[p] = max(siz[p], 1ll);
}
void solve()
{
cin >> n >> s;
for (int i = 1; i <= n; i++)
g[i].clear(), w[i] = siz[i] = 0;
for (int i = 1; i < n; i++)
{
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
dfs1(1, 0);
struct item
{
int w;
int c;
void cut()
{
w = w / 2;
}
int eval() const
{
return c * (w - w / 2);
}
bool operator<(const item &rhs) const
{
return eval() < rhs.eval();
}
};
priority_queue<item> heap;
int ans = 0;
int sum = 0;
for (int i = 2; i <= n; i++)
{
heap.push({w[i], siz[i]});
sum += w[i] * siz[i];
}
while (sum > s)
{
auto cur = heap.top();
heap.pop();
sum -= cur.eval();
cur.cut();
heap.push(cur);
ans++;
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--)
{
solve();
}
}