• [ZJOI2007] 时态同步(树形dp)


    传送门

    很好的一道树形dp的题目
    • 根据所给题意可以总结为:从激励点出发求到所有叶子节点时间相同的最小次数

    • 由于要找最小次数,考虑贪心的方法,要使次数尽量小,也就是要使子节点的公共边尽可能的大。

    • 有一点可以肯定的是一定要知道时间最长是多少,然后其他树枝才能根据这个时间求最小值,而这个最长时间是很容易求得的。

    • 求完最长时间后,考虑其他树枝,如何判断公共树枝要加多少呢?方法就是知道从它的最下面的叶子节点到它这里最远要多长时间。知道最远时间后,就能求得一个时间差,要让这个节点内的所有点的权值都相同,这个差也就是将这个节点最大的一个子节点依次减去其他节点的权值,然后将这些差值累加起来。最后的答案就是这个统计的差值。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 5e5 + 10;
    typedef long long ll;
    bool vis[N];
    ll n, tot, s, head[N], dp[N], maxn[N], ans;
    
    struct Edge{
    	ll v, w, next;
    }edge[N * 2];
    
    void add(ll u, ll v, ll w){
    	edge[tot].v = v;
    	edge[tot].w = w;
    	edge[tot].next = head[u];
    	head[u] = tot ++;
    }
    
    void dfs(int now, int fa){
    	for(int i = head[now]; i != -1; i = edge[i].next){
    		int v = edge[i].v;
    		int w = edge[i].w;
    		if(v == fa)
    			continue;
    		dfs(v, now);
    		dp[now] = max(dp[now], dp[v] + w);		//找出当前节点的时间最长的一个节点
    	}
    	for(int i = head[now]; i != -1; i = edge[i].next){
    		int v = edge[i].v;
    		int w = edge[i].w;
    		if(v == fa)
    			continue;
    		ans += (dp[now] - (dp[v] + w));			//统计差值
    	}
    }
    int main(){
    	scanf("%lld%lld", &n, &s);	
    	memset(head, -1, sizeof head);
    	for(int i = 1; i < n; i ++){
    		ll a, b, w;
    		scanf("%lld%lld%lld", &a, &b, &w);
    		add(a, b, w);
    		add(b, a, w);
    	}
    
    	dfs(s, 0);
    
    	printf("%lld
    ", ans);
    	return 0;
    } 
    
  • 相关阅读:
    JZOJ 5728. 简单计数|| (容斥+动态规划)
    6638. 【GDOI2020.5.16模拟】Seat (队列)
    JZOJ 5750. 青青草原播种计划 (小性质+线段树)
    JZOJ 5753. 完全二叉树 (可持久化线段树维护hash值)
    JS框架-React.js
    flexbox弹性盒子布局
    压缩js和css文件的原理
    JS判断数据类型的方式
    JS数据类型
    ES6新特性
  • 原文地址:https://www.cnblogs.com/pureayu/p/14984382.html
Copyright © 2020-2023  润新知