nssl1163-小x遊世界樹【樹形dp,二次掃描和換根法】
阿新 • • 發佈:2018-12-11
正題
題目大意
一棵樹,一條邊的權是原本的權值減去出發點的加速。 求一個點使得這個點到所有點路徑邊權和最小。
解題思路
我們先求出以1為根時的答案 然後用換根法 我們從1轉移到2,我們會發現 紅色的部分的路徑都減去的紫色的路徑長度,藍色的部分路徑長度都加上這條紫色的路徑(注意因為出發點不同所以權值不同) 所以我們推出根轉移方程
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]);
}