1. 程式人生 > >BZOJ1060 時態同步 [樹形DP]

BZOJ1060 時態同步 [樹形DP]

oid 產生 blog 組成 pre 學習 連接線 scanf 編號

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\n",ans);
    return 0;
}

BZOJ1060 時態同步 [樹形DP]