zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第九场)B

    Description

    有一棵 (n) 个点的树,从 (1) 号点出发,经过一条边的时候会减少一定的 HP,每条边最多只能经过两次。首次到达某个点 (i) 会增加 (a[i]) 点 HP。求初始 HP 至少为多少才能保证存在一种能遍历所有点并回到原点的方案且整个过程中的 HP 非负。

    Solution

    不妨设访问 (i) 的子树的代价总和为 (s[i]),设访问 (i) 的子树过程中可能出现的最小 HP 的最大值(可能是负数)为 (f[i]),则转移时需要枚举访问各个子树的顺序。

    考虑引入临项交换排序的思想,对于 (i,j=i+1),如果 (i o j)(j o i) 优,那么一定有 (min(f_j, s_j+f_i) = min (f_i,s_i+f_j))

    为了能分离出排序使用的关键字,暴力展开上式,发现可以将所有孩子分成 (A={i | i ge 0}, B=E-A) 两部分,那么显然对于 (a in A,b in B)(a o b) 是更优的,于是只需要考虑 (A,B) 各自内部的顺序。根据展开结果容易得出,(A) 内需要 (f_i > f_j)(B) 内需要 (s_i - f_i > s_j - f_j)。实际实现时可以放在一起处理。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long 
    const int N = 1000005;
    
    int f[N],s[N],a[N],vis[N],n,m,faw[N];
    
    vector <pair<int,int>> g[N];
    
    int t1,t2,t3;
    
    void clear()
    {
        for(int i=1;i<=n;i++)
        {
            f[i]=s[i]=a[i]=vis[i]=faw[i]=0;
            g[i].clear();
        }
    }
    
    bool cmp(int i,int j)
    {
        if(s[i]>=0 && s[j]<0) return 1;
        if(s[i]>=0)
        {
            return f[i]>f[j];
        }
        if(s[j]<0)
        {
            return s[i]+f[j]>s[j]+f[i];
        }
        return 0;
    }
    
    
    void dfs(int p)
    {
        vis[p]=1;
        s[p]=a[p];
        vector <int> vec;
        for(auto pr:g[p])
        {
            int q=pr.first,w=pr.second;
            if(vis[q]==0)
            {
                dfs(q);
                s[q]-=w*2;
                f[q]-=w;
                f[q]=min(f[q],s[q]);
                vec.push_back(q);
            }
        }
        sort(vec.begin(),vec.end(),cmp);
        for(auto i:vec)
        {
            f[p]=min(f[p],s[p]+f[i]);
            s[p]+=s[i];
        }
    }
    
    
    signed main()
    {
        ios::sync_with_stdio(false);
    
        int t;
        cin>>t;
        while(t--)
        {
            cin>>n;
            for(int i=1;i<=n;i++) cin>>a[i];
            for(int i=1;i<n;i++)
            {
                cin>>t1>>t2>>t3;
                g[t1].push_back({t2,t3});
                g[t2].push_back({t1,t3});
            }
            dfs(1);
            cout<<max(0ll,-f[1])<<endl;
            clear();
        }
        return 0;
    }
    
  • 相关阅读:
    判断一个图是否有环 无向图 有向图
    经典算法解析
    图的深度优先遍历DFS(邻接矩阵表示法)
    Prim算法求最小生成树MST以及和kruskal算法的对比
    oracle连接中出现错误ORA12541,ORA12514,ORA01017的解决方法
    visual studio 自动整理代码
    解决Google code和Google group无法登录的问题
    Dijkstra算法求单源最短路径(二)(BFS的改版)
    快速排序算法QuickSort
    推荐资料
  • 原文地址:https://www.cnblogs.com/mollnn/p/13770240.html
Copyright © 2011-2022 走看看