[洛谷P1131] ZJOI2007 時態同步
問題描述
小Q在電子工藝實習課上學習焊接電路板。一塊電路板由若幹個元件組成,我們不妨稱之為節點,並將其用數
字1,2,3….進行標號。電路板的各個節點由若幹不相交的導線相連接,且對於電路板的任何兩個節點,都存在且僅
存在一條通路(通路指連接兩個元件的導線序列)。在電路板上存在一個特殊的元件稱為“激發器”。當激發器工
作後,產生一個激勵電流,通過導線傳向每一個它所連接的節點。而中間節點接收到激勵電流後,得到信息,並將
該激勵電流傳向與它連接並且尚未接收到激勵電流的節點。最終,激烈電流將到達一些“終止節點”——接收激勵
電流之後不再轉發的節點。激勵電流在導線上的傳播是需要花費時間的,對於每條邊e,激勵電流通過它需要的時
得到激勵電路——即保持時態同步。由於當前的構造並不符合時態同步的要求,故需要通過改變連接線的構造。目
前小Q有一個道具,使用一次該道具,可以使得激勵電流通過某條連接導線的時間增加一個單位。請問小Q最少使用多少次道具才可使得所有的“終止節點”時態同步?
輸入格式
第一行包含一個正整數N,表示電路板中節點的個數。第二行包含一個整數S,為該電路板的激發器的編號。接下來N-1行,每行三個整數a , b , t。表示該條導線連接節點a與節點b,且激勵電流通過這條導線需要t個單位時間。
輸出格式
僅包含一個整數V,為小Q最少使用的道具次數。
樣例輸入
3
1
1 2 1
1 3 3
樣例輸出
2
題解
從輸入就可以看出,該題給定的圖是一棵樹。那麽激發器可以看做是該樹的根節點,終止節點可以看做是樹的葉子節點。我們利用DFS框架實現樹上DP。假設我們已經知道一個點的節點的所有子節點同時到達葉子節點所需要的時間,那麽該點的電流同時到達葉子節點的時間就是所有子節點的最大值。既然確定了時間,那麽需要增加的時間就是最大時間與每一個子節點的時間的差。此外,該點的父節點流過來的電流要同時到達葉子節點至少需要的時間就是流到該點的時間再加上該點需要的時間。以此類推,就可以知道從根節點出發所需要的時間了。
註意,在這種方法下需要在回溯前統計答案而不是在遞歸前(畢竟你要先知道子節點的時間)。
代碼
#include <iostream>
#include <cstdio>
#define N 500002
using namespace std;
long long head[N],ver[N*2],nxt[N*2],edge[N*2],l;
long long n,s,i,f[N],ans;
void insert(int x,int y,int z)
{
l++;
ver[l]=y;
edge[l]=z;
nxt[l]=head[x];
head[x]=l;
}
void dfs(int x,int pre)
{
int y;
for(int i=head[x];i;i=nxt[i]){
y=ver[i];
if(y!=pre) dfs(y,x);
}
for(int i=head[x];i;i=nxt[i]){
y=ver[i];
if(y!=pre) f[x]=max(f[x],edge[i]);
}
for(int i=head[x];i;i=nxt[i]){
y=ver[i];
if(y!=pre) ans+=f[x]-edge[i];
}
for(int i=head[pre];i;i=nxt[i]){
if(ver[i]==x){
edge[i]+=f[x];
break;
}
}
}
int main()
{
cin>>n>>s;
for(i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
insert(u,v,w);
insert(v,u,w);
}
dfs(s,0);
cout<<ans<<endl;
return 0;
}
[洛谷P1131] ZJOI2007 時態同步