1. 程式人生 > >CS academy Growing Trees【模板】DP求樹的直徑

CS academy Growing Trees【模板】DP求樹的直徑

algorithm wing trees 一次 gis getc 範圍 aca sig

技術分享圖片

【題意概述】

  給出一棵樹,樹上的邊有兩個值a和b,你可以在[0,limit]範圍內選擇一個整數delta,樹上的邊的權值為a+b*delta,現在問當delta為多少的時候樹的直徑最小、最小直徑是多少。

【題解】

  每條邊的邊權都是一次函數,那麽直徑是單峰函數。單峰函數求最小值我們可以用三分法。

  註意邊權可能為負數,求直徑時要用DP,而不能用dfs到最遠點、再從最遠點dfs到它的最遠點的方法。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define LL long long
 5
#define rg register 6 #define N 500010 7 using namespace std; 8 int n,m,tot,del,l,r,last[N]; 9 LL ans=3e18,mx,dis[N][2]; 10 struct edge{ 11 int to,pre; LL w; 12 }e[N<<1]; 13 struct rec{ 14 int u,v,a,b; 15 }re[N]; 16 inline int read(){ 17 int k=0,f=1; char c=getchar(); 18 while(c<
0||c>9)c==-&&(f=-1),c=getchar(); 19 while(0<=c&&c<=9)k=k*10+c-0,c=getchar(); 20 return k*f; 21 } 22 void dfs(int x,int fa){ 23 for(rg int i=last[x],to;i;i=e[i].pre)if((to=e[i].to)!=fa){ 24 dfs(to,x); LL tmp=dis[to][0]+e[i].w; 25 if(tmp>=dis[x][0
]){ 26 dis[x][1]=dis[x][0],dis[x][0]=tmp; 27 } 28 else 29 if(tmp>dis[x][1]) dis[x][1]=tmp; 30 } 31 mx=max(mx,max(dis[x][0],dis[x][0]+dis[x][1])); 32 } 33 inline LL calc(int x){ 34 memset(last,0,sizeof(last)); tot=0; 35 for(rg int i=1;i<n;i++){ 36 int u=re[i].u,v=re[i].v; LL w=re[i].a+re[i].b*x; 37 e[++tot]=(edge){u,last[v],w}; last[v]=tot; 38 e[++tot]=(edge){v,last[u],w}; last[u]=tot; 39 } 40 memset(dis,0,sizeof(dis)); mx=0; 41 dfs(1,0); 42 return mx; 43 } 44 signed main(){ 45 n=read(); l=-1; r=read(); 46 for(rg int i=1;i<n;i++){ 47 re[i].u=read(); re[i].v=read(); re[i].a=read(); re[i].b=read(); 48 } 49 while(l+1<r){ 50 int mid1=(l+r)>>1,mid2=mid1+1; 51 if(calc(mid1)<=calc(mid2)) r=mid1; 52 else l=mid1; 53 } 54 printf("%d\n%lld\n",r,calc(r)); 55 return 0; 56 }

CS academy Growing Trees【模板】DP求樹的直徑