CF-1515-F-思维

1515-F 题目大意

给定一个n个点和m条边的连通图和一个整数x,点有点权ai,权值非负。如果一条边(u,v)满足au+avx则可以把u,v缩成一个点,新点的点权为au+avx,判断这个图是否能够缩成一个点,如果可以,请依次输出每条边。


Solution

首先,如果所有点权和小于(n1)x,那么一定无解。

否则,一定存在一种合法的方案,证明如下:

先随便选择一个叶子u, 其点权为au,分情况考虑:

1.aux,则此时修建点u与其父亲p之间的边,其父亲的点权更新为au+apx,然后把它们缩成一点,剩下的树是一颗n1个节点的树,且满足条件。

2.au<x,则把它和它父亲p之间的边放到最后修建,然后考虑剩余的(n1)个点来修建,仍然是一颗满足条件的树。

基于这个结论,做法如下:对整个树dfs,枚举到点u,其父亲为p,以u为根的子树已经访问完毕,

  • 如果aux,则直接修建这条边,记录到答案中。
  • 否则把u集合,用栈维护,放到最后修建。

最后把栈中的边依次记录到答案中输出即可,时间复杂度O(n)

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

void solve(){
    int n,m,x;
    cin>>n>>m>>x;
    vector<ll> a(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    vector<vector<pair<int,int>>> e(n+1);
    vector<int> vis(n+1);
    for(int i=0;i<m;i++){
        int x,y;
        cin>>x>>y;
        e[x].push_back({y,i+1});
        e[y].push_back({x,i+1});
    }
    if(accumulate(a.begin(),a.end(),0LL)<1LL*(n-1)*x){
        cout<<"NO";
        return;
    }
    vector<int> ans,st;
    function<void(int,int,int)> dfs=[&](int u,int fa,int i){
        vis[u]=1;
        for(auto [v,j]:e[u]){
            if(vis[v]) continue;
            dfs(v,u,j);
        }
        if(u!=1){
            if(a[u]>=x){
                ans.push_back(i);
                a[fa]+=a[u]-x;
            }else{
                st.push_back(i);
            }
        }
    };
    dfs(1,0,0);
    for(int i=st.size()-1;~i;i--){
        ans.push_back(st[i]);
    }
    cout<<"YES"<<'\n';
    for(int i=0;i<n-1;i++){
        cout<<ans[i]<<'\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  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示