【题意概述】
给出一棵树,树上的边有两个值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 %lld ",r,calc(r)); 55 return 0; 56 }