• 2020牛客多校第九场


    B.Groundhog and Apple Tree

    显然,在中途恢复体力等价于在根节点将所有需要的体力恢复完再走。那么我们考虑树DP。(dp[u])表示遍历完(u)所在子树最少需要在(u)恢复的HP。(res[u])表示遍历(u)这个子树的收益(正就是获得HP,负就是需要HP)。我们需要确定的是一种访问子树的顺序。我们假设确定了一种访问顺序,考虑计算在此种情况下需要的恢复时间(need)。我们转移(dp[u])时,记录一个(sum[v]=res[v] - 2*w_{u,v}),表示从(u)点去访问子树(v)的收益。对于第(i)个访问的子树,我们需要起初至少有(H[i])点HP
    那么(H[i]=cost[i]-sum_{j=1}^{i-1} sum[j])。其中(cost[i])表示从(u)遍历(i)子树需要的代价。
    对于(cost[i])的计算,有(cost[i]=dp[i]+w_{u,i}+delta),访问子树(i)的时候,可能从(i)回到(u)HP不够,需要一个(delta)的HP。我们如果在(u)恢复了(dp[i]+w_{u,i})的体力后访问子树,到(i)准备返回(u)的时候剩余(HP=dp[i]+w_{u,i}-w_{u,i}+res[i]=dp[i]+res[i]),如果(dp[i]+res[i]geq w_{u,i})(delta=0),否则(delta=w_{u,i}-dp[i]-res[i])
    此时求出每个(cost[i])(H[i])。那么可以得到(need=max_{i=1}^{son[u]} H[i])。对于所有的排列方式,我们有(dp[u]=min(need))
    如果某个访问顺序是最优的,那么交换任意(i)(i+1)都会不优。考虑交换(i)(i+1),改变的只有(H[i],H[i+1]),所以要有(max(cost[i]-sum_{j=1}^{i-1}sum[j],cost[i+1]-sum_{j=1}^{i} sum[j])leq max(cost[i+1]-sum_{j=1}^{i-1} sum[j] , cost[i]-sum_{j=1}^{i-1}sum[j]-sum[i+1])).方便观察,我们全部消去一个(A=sum_{j=1}^{i-1}sum[j]).原式化简为(max(cost[i],cost[i+1]-sum[i])leq max(cost[i+1],cost[i]-sum[i+1])).我们进行分类讨论。

    (1.sum[i]<0,sum[i+1]<0),那么会有(cost[i+1]-sum[i]leq cost[i]-sum[i+1])(cost[i]+sum[i]geq cost[i+1]+sum[i+1]).

    (2.sum[i]geq 0,sum[i+1]geq 0),那么会有(cost[i]leq cost[i+1])

    (3.sum[i]<0,sum[i+1]geq 0),恒不成立

    (4.sum[i]geq 0,sum[i+1]<0),恒成立

    综上,我们先讲所有子节点分成两类分别排序就可以确定访问顺序了。

    #include <bits/stdc++.h>
    #define pb emplace_back
    #define ll long long
    #define lson rt << 1
    #define rson rt << 1 | 1
    #define all(x) (x).begin(),(x).end()
    #define pii pair<int,int>
    #define pll pair<ll,ll>
    using namespace std;
    
    const int maxn = 2e5;
    ll sum[maxn + 11],dp[maxn + 11],res[maxn + 11];
    int to[maxn + 11],w[maxn + 11],front[maxn + 11],nxt[maxn + 11],a[maxn + 11];
    int cnt = 0;
    
    void add(int u,int v,int _) { to[++cnt] = v; w[cnt] = _; nxt[cnt] = front[u]; front[u] = cnt; }
    
    void dfs(int x,int fa) {
    	sum[x] = a[x]; res[x] = a[x];
    	for (int i = front[x]; i ; i = nxt[i]) {
    		int v = to[i] , _ = w[i];
    		if (v == fa) continue;
    		dfs(v , x);
    		sum[v] -= 2 * _; sum[x] += sum[v];
    		res[x] -= 2 * _; res[x] += res[v];
    	}
    }
    
    void solve(int x,int fa) {
    	dp[x] = 0;
    	vector <pll> A,B; ll cost = 0 , sal = 0;
    	for (int i = front[x]; i ; i = nxt[i]) {
    		int v = to[i] , _ = w[i];
    		if (v == fa) continue;
    		solve(v , x);
    		cost = dp[v] + _; if (res[v] + dp[v] - _ < 0) cost -= res[v] + dp[v] - _;
    		sal = sum[v];
    		if (sal >= 0) A.pb(make_pair(cost , sal)); else B.pb(make_pair(cost , sal));
    	}
    	sort(all(A) , [&](pll x,pll y) { return x.first < y.first; } );
    	sort(all(B) , [&](pll x,pll y) { return x.first + x.second > y.first + y.second; } );
    	ll s = 0;
    	for (auto pi : A) dp[x] = max(dp[x] , pi.first - s) , s += pi.second;
    	for (auto pi : B) dp[x] = max(dp[x] , pi.first - s) , s += pi.second;
    	dp[x] -= a[x]; dp[x] = max(dp[x] , 0ll);
    } 
    
    int main(){ 
    	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    	int t; cin >> t;
    	while (t--) {
    		int n; cin >> n;
    		for (int i = 1; i <= n; i++) cin >> a[i] , front[i] = 0;
    		cnt = 0;
    		for (int i = 1; i < n; i++) {
    			int u , v , w; cin >> u >> v >> w;
    			add(u , v , w); add(v , u , w);
    		} 
    		dfs(1 , 0);
    		solve(1 , 0);
    		cout << dp[1] << endl;
    	}
    }
  • 相关阅读:
    JAVA课程课后作业之使用递归完成回文
    原码、补码,反码以及JAVA中数值采用哪种码表示
    JAVA课后作业01
    java开学考试有感以及源码
    自动生成30道小学四则运算题目
    统计文档中单词出现频率
    JAVA课程课后作业03之动手动脑
    暑假假期周进度报告(第八周)
    字节流FileInputStream FileOutputStream四种读写,包装流BufferedOutputStream BufferedInputSream 缓冲区默认8192字节
    多态性
  • 原文地址:https://www.cnblogs.com/Embiid/p/13561482.html
Copyright © 2020-2023  润新知