CF-1399-E2-优先队列

1399-E2 题目大意

给定一棵n个节点的树,边带权,根节点为1。再给定一个整数S,你可以执行以下操作:

  • 选择一条权值为wi的边,令wiwi2

你可以执行任意次操作,使得xleavessum(1,x)不大于S,其中sum(1,x)表示x到根节点的路径上的边权和。


Solution

一遍dfs求出各个边对答案的贡献,接下来依次执行操作,直到边权和小于等于S

每次操作应当选取能够减少贡献最大的边,可以用优先队列来选取要操作的边。对于每个边在优先队列中的key应当是(wiwi2)cntcnt为该边对答案的贡献次数。操作完后还需把边权减半重新加入优先队列中。

每个边最多经过logU次操作就会变为0,这里的时间复杂度为O(nlognlogU)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;

void solve(){
    int n;
    ll S;
    cin>>n>>S;
    vector<vector<pair<int,int>>> e(n);
    for(int i=1;i<n;i++){
        int x,y,w;
        cin>>x>>y>>w;
        --x,--y;
        e[x].push_back({y,w});
        e[y].push_back({x,w});
    }
    ll cost=0;
    vector<int> sz(n);
    priority_queue<tuple<ll,int,int>> q;
    function<void(int,int)> dfs=[&](int x,int fa){
        if(e[x].size()==1&&e[x][0].first==fa){
            sz[x]=1;
            return;
        }
        for(auto [y,w]:e[x]){
            if(y==fa) continue;
            dfs(y,x);
            sz[x]+=sz[y];
            cost+=1LL*w*sz[y];
            q.push({1LL*(w-w/2)*sz[y],w/2,sz[y]});
        }
    };
    dfs(0,-1);
    int ans=0;
    while(cost>S){
        auto [d,w,c]=q.top();
        q.pop();
        cost-=d;
        q.push({1LL*(w-w/2)*c,w/2,c});
        ans++;
    }
    cout<<ans<<'\n';
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    int T=1;
    cin>>T;
    while(T--){
        solve();
    }
    return 0;
}
posted @   fengxue-K  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示