2018年9月24日提高组模拟赛 T2 小x游世界树

大意

给定一棵树,求出从哪个点跑最短路使得最短路径的和最小


思路

二次扫描换根法

先用一遍 d f s dfs dfs求出一个点的最短路,然后考虑换根带来的最短路影响

以样例为例,假设我们现在要从1换根到2
干得漂亮
我们考虑换根会带来的影响,发现1和3(绿点)多走了2那条边,而2和4少走1(红点)那条边
无所不有
这样我们就得到了状态转移方程
f [ y ] = f [ x ] + ( n − n u m [ y ] ) × e [ i ] . w − n u m [ y ] × e [ i 1 ] . w f[y]=f[x]+(n-num[y])\times e[i].w-num[y]\times e[i^1].w f[y]=f[x]+(nnum[y])×e[i].wnum[y]×e[i1].w

f [ 儿 子 ] = f [ 父 亲 ] + 父 亲 其 他 儿 子 的 子 节 点 数 × 儿 子 连 向 父 亲 的 边 − 儿 子 的 子 节 点 数 × 父 亲 连 向 儿 子 的 边 f[儿子] =f[父亲]+父亲其他儿子的子节点数 \times 儿子连向父亲的边-儿子的子节点数\times 父亲连向儿子的边 f[]=f[]+××

通不通俗,易不易懂


代码

#pragma GCC optimize(2)
#include<cstring>
#include<cstdio>
#define ri register int
#define r(i,a,b) for(register int i=a;i<=b;i++)
using namespace std;const int N=1400001;
int n,l[N],tot=1,yh[N],x,y,z,ans=1,num[N];
struct node{int next,to,w;}e[N<<1];
inline void add(ri u,ri v,ri w){e[++tot]=(node){l[u],v,w};l[u]=tot;return;}
bool vis[N]={0};
long long f[N];
inline int read()//输入优化
{
	int f=0,d=1;char c;
	while(c=getchar(),c<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),c>47&&c<58)f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
inline void write(long long x)//输出优化
{
	if(x<0) {x=-x;putchar('-');}
	if(x>9)write(x/10);putchar(x%10+48);
	return;
}
inline void dfs(ri x)//求单点最短路
{
	vis[x]=true;num[x]++;
	for(register int i=l[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if(!vis[y])
		{
			dfs(y);
			num[x]+=num[y];
			f[1]+=e[i].w*num[y];
		}
	}
	return;
}
inline void dp(ri x)//换根
{
	vis[x]=true;
	for(register int i=l[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if(!vis[y])
		{
			f[y]=f[x]+(n-num[y])*(e[i^1].w)-num[y]*(e[i].w);
			dp(y);
			if(f[y]<f[ans]) ans=y;
			if(f[y]==f[ans]) if(y<ans) ans=y;
		}
	}
	return;
}
signed main()
{
	n=read();
	r(i,1,n) yh[i]=read();
	r(i,1,n-1)
	{
		x=read();y=read();z=read();
		add(x,y,z-yh[x]);add(y,x,z-yh[y]);
	}
	dfs(1);
	memset(vis,0,sizeof(vis));
	dp(1);
	write(ans);putchar(10);write(f[ans]);//输出
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值