nssl1163-小x游世界树【树形dp,二次扫描和换根法】

正题


题目大意

一棵树,一条边的权是原本的权值减去出发点的加速。
求一个点使得这个点到所有点路径边权和最小。


解题思路

我们先求出以1为根时的答案
然后用换根法
在这里插入图片描述
我们从1转移到2,我们会发现
在这里插入图片描述
红色的部分的路径都减去的紫色的路径长度,蓝色的部分路径长度都加上这条紫色的路径(注意因为出发点不同所以权值不同)
所以我们推出根转移方程
f y = f x − n u m y ∗ ( w − m o v x ) + ( n − n u m y ) ∗ ( w − m o v y ) f_y=f_x-num_y*(w-mov_x)+(n-num_y)*(w-mov_y) fy=fxnumy(wmovx)+(nnumy)(wmovy)


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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值