1. 程式人生 > >nssl1163-小x遊世界樹【樹形dp,二次掃描和換根法】

nssl1163-小x遊世界樹【樹形dp,二次掃描和換根法】

正題

題目大意

一棵樹,一條邊的權是原本的權值減去出發點的加速。 求一個點使得這個點到所有點路徑邊權和最小。

解題思路

我們先求出以1為根時的答案 然後用換根法 在這裡插入圖片描述 我們從1轉移到2,我們會發現 在這裡插入圖片描述 紅色的部分的路徑都減去的紫色的路徑長度,藍色的部分路徑長度都加上這條紫色的路徑(注意因為出發點不同所以權值不同) 所以我們推出根轉移方程 fy=fxnumy(wmovx)+(nnumy)(wmovy)f_y=f_x-num_y*(w-mov_x)+(n-num_y)*(w-mov_y)

code

#include
<cstdio>
#include<algorithm> #include<cstring> #define ll long long #define N 700010 using namespace std; struct line{ ll to,w,next; }a[N*2]; ll ls[N],mov[N],x,y,w,n,f[N],num[N],tot,ans; int read(){ char c=getchar();int x=0; for(;'0'>c||c>'9';c=getchar()); for(;'0'<=
c&&c<='9';c=getchar()) x=x*10+(c-'0'); return x; } void addl(ll x,ll y,ll w) { a[++tot].to=y;a[tot].w=w; a[tot].next=ls[x];ls[x]=tot; } void dp(ll x,ll fa)//計算第一個答案和子樹大小 { num[x]=1; for(ll i=ls[x];i;i=a[i].next) { ll y=a[i].to; if(y!=fa) { dp(y,x); f[1]+=max(a[i].w-mov[x]
,0ll)*num[y]; num[x]+=num[y]; } } } void dp2(ll x,ll fa)//轉移根 { if(f[x]<f[ans]||(f[x]==f[ans]&&x<ans)) ans=x;//統計答案 for(ll i=ls[x];i;i=a[i].next) { ll y=a[i].to; if(y!=fa) { f[y]=f[x]-num[y]*(a[i].w-mov[x])+(n-num[y])*(a[i].w-mov[y]); //動態轉移 dp2(y,x); } } } int main() { n=read(); for(ll i=1;i<=n;i++) mov[i]=read(); for(ll i=1;i<n;i++) { x=read();y=read();w=read(); addl(x,y,w);addl(y,x,w); } dp(1,0); ans=1; dp2(1,0); printf("%lld\n%lld",ans,f[ans]); }