zoukankan      html  css  js  c++  java
  • BZOJ1060 时态同步 [树形DP]

    Description

    小Q在电子工艺实习课上学习焊接电路板。一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字1,2,3….进行标号。电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点,都存在且仅存在一条通路(通路指连接两个元件的导线序列)。

    在电路板上存在一个特殊的元件称为“激发器”。当激发器工作后,产生一个激励电流,通过导线传向每一个它所连接的节点。而中间节点接收到激励电流后,得到信息,并将该激励电流传向与它连接并且尚未接收到激励电流的节点。最终,激烈电流将到达一些“终止节点”――接收激励电流之后不再转发的节点。

    激励电流在导线上的传播是需要花费时间的,对于每条边e,激励电流通过它需要的时间为te,而节点接收到激励电流后的转发可以认为是在瞬间完成的。现在这块电路板要求每一个“终止节点”同时得到激励电路――即保持时态同步。由于当前的构造并不符合时态同步的要求,故需要通过改变连接线的构造。目前小Q有一个道具,使用一次该道具,可以使得激励电流通过某条连接导线的时间增加一个单位。请问小Q最少使用多少次道具才可使得所有的“终止节点”时态同步?

    Input

    第一行包含一个正整数N,表示电路板中节点的个数。

    第二行包含一个整数S,为该电路板的激发器的编号。

    接下来N-1行,每行三个整数a , b , t。表示该条导线连接节点a与节点b,且激励电流通过这条导线需要t个单位时间。

    Output

    仅包含一个整数V,为小Q最少使用的道具次数。

    思路

    看输入数据,由于n个节点只有n-1条边,不难看出这是一棵树。我们可以反着思考,就是让所有叶子节点同时发出信号,然后这些信号同时到达根节点。于是我们可以自下而上的进行维护,使得每一节点所有子节点的信号同时到达该节点。

    于是我们考虑如何维护。我们从根节点开始搜索,搜索到叶子节点,回溯的时候进行维护,先维护节点的所有子节点到该节点最大边权(边权为叶子节点到同时到达它所需要时间)。然后维护答案,答案为最大边权减去所有到子节点的边权。然后维护父节点的边权,父节点边权为该节点子节点的 最大边权+父节点到该节点的时间。然后就回溯,重复操作,到根节点为止。好难说清楚啊QWQ 看注释更明白一点

    然后我们要注意一些细节:

    1. 一定要双向加边,是无向图。
    2. 既然是无向图,维护时不要把到父节点的边计算了。
    3. 维护的顺序一定不能乱。
    4. 答案要用long long 存。

    代码

    #include <bits/stdc++.h>
    #define MAXN 1000005
    using namespace std;
    struct Edge{int next,to,dis;} edge[MAXN];
    int n,s,a,b,t,maxn[MAXN],cnt,head[MAXN];  //maxn储存到子节点的最大边权
    long long ans;  //注意,答案要用long long 存
    
    void addedge(int from, int to, int dis)  
    {
        edge[++cnt].next=head[from];
        edge[cnt].to=to;
        edge[cnt].dis=dis;
        head[from]=cnt;
    }  //前向星加边
    
    void dfs(int x, int fa) //X为当前搜索节点,fa为x的父亲节点
    {
        for(int i=head[x]; i; i=edge[i].next)
            if(edge[i].to!=fa) dfs(edge[i].to, x);
        //这一句一定要最先,先搜索到底层,回溯时再进行后续处理(从下向上维护)
        for(int i=head[x]; i; i=edge[i].next)
            if(edge[i].to!=fa) maxn[x]=max(maxn[x], edge[i].dis);
        //维护到子节点的最大边权
        for(int i=head[x]; i; i=edge[i].next)
        	if(edge[i].to!=fa) ans+=(maxn[x]-edge[i].dis);
        //维护答案
        for(int i=head[fa]; i; i=edge[i].next)
        	if(edge[i].to==x) edge[i].dis+=maxn[x];
        //这一句不能漏,更新父节点到该节点的边权
    }//注意顺序不能乱
    
    int main()
    {
        scanf("%d%d",&n,&s);
        for(int i=1; i<=n-1; i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            addedge(a, b, t);
            addedge(b, a, t); //是无向图,双向加边
        }
        dfs(s, 0);
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    宿主机( win 7 系统) ping 虚拟机VMware( cent os 6.6 ) 出现“请求超时”或者“无法访问目标主机”的解决方法
    Java实现 LeetCode 23 合并K个排序链表
    Java实现 LeetCode 23 合并K个排序链表
    Java实现 LeetCode 23 合并K个排序链表
    Java实现 LeetCode 22 括号生成
    Java实现 LeetCode 22 括号生成
    Java实现 LeetCode 22 括号生成
    Java实现 LeetCode 21 合并两个有序链表
    Java实现 LeetCode 21 合并两个有序链表
    Java实现 LeetCode 21 合并两个有序链表
  • 原文地址:https://www.cnblogs.com/CrazyDave/p/8442262.html
Copyright © 2011-2022 走看看