版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82822426
大意
给定一棵树,求出从哪个点跑最短路使得最短路径的和最小
思路
二次扫描换根法
先用一遍 求出一个点的最短路,然后考虑换根带来的最短路影响
以样例为例,假设我们现在要从1换根到2
我们考虑换根会带来的影响,发现1和3(绿点)多走了2那条边,而2和4少走1(红点)那条边
这样我们就得到了状态转移方程
通不通俗,易不易懂
代码
#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]);//输出
}