CF1399E1题解报告
前言
这里使用 vector 存树,别喷。
正文
思路
首选贪心,每次修改都会让总和减一个值,我们只需要每次操作时让这个值最大即可。
不难发现,每次减多少与该边出现过多少次有密切的关系,所以我们可以统计每条边出现多少次,并用结构体存储(等下你就知道为什么了),但是怎么统计呢?
不难发现,\(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;
}
完结撒花