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

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82822426

大意

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


思路

二次扫描换根法

先用一遍 d f s 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 [ ] = f [ ] + × × f[儿子] =f[父亲]+父亲其他儿子的子节点数 \times 儿子连向父亲的边-儿子的子节点数\times 父亲连向儿子的边

通不通俗,易不易懂


代码

#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]);//输出
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82822426