CS academy Growing Trees【模板】DP求树的直径

【题意概述】

  给出一棵树,树上的边有两个值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 }

 

posted @ 2018-10-31 14:42  Driver_Lao  阅读(211)  评论(0编辑  收藏  举报