1. 程式人生 > >2018年9月24日提高組模擬賽 T2 小x遊世界樹

2018年9月24日提高組模擬賽 T2 小x遊世界樹

大意

給定一棵樹,求出從哪個點跑最短路使得最短路徑的和最小

思路

二次掃描換根法

先用一遍dfsdfs求出一個點的最短路,然後考慮換根帶來的最短路影響

以樣例為例,假設我們現在要從1換根到2 幹得漂亮 我們考慮換根會帶來的影響,發現1和3(綠點)多走了2那條邊,而2和4少走1(紅點)那條邊 無所不有 這樣我們就得到了狀態轉移方程 f[y]=f[x]+(nnum[y])×e[i].wnum[y]×e[i1].wf[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]);//輸出 }