题意
给定一棵 (n) 个点的树,给定 (k) ,求 (|frac{sum w(路径长度)}{t(路径边数)}-k|)的最小值。
(nleq 5 imes 10^5,kleq 10^{13})
分析
看到分数考虑分数规划,二分答案 (x),式子转化成 (-x< frac{sum w}{t}-k< x)
将边权变为 (w-k) 消除 (k) 的影响。但是不能够直接求最长链。因为是路径,考虑点分治。
二分答案 (x) 之后考虑两条路径组合 ((A_1,B_1),(A_2,B_2)),其中 (A) 表示路径长度,(B) 表示路径边数。
有 (-x<frac{A_1+A_2}{B_1+B_2}< x) ,当 (A_1+A_2 > 0) 时只用考虑 (< x) 的条件,得到 (A_1-B_1x< B_2x-A_2),反之同理。
现在考虑 (A_1+A_2 > 0) 的情况。先将所有路径按照 (A) 排序后从左边开始枚举路径,然后用一个指针从右往左维护所有 (A_1+A_2> 0) 的路径,然后维护 (Bx-A) 的最小值。但是有可能最小值和当前枚举的路径相同,所以再记一个次小值。
由于要下取整,先求出 (> ans) 的最小整数解然后 (-1) 。
总时间复杂度为 (O(nlog^2n))。
## 代码
~~~cpp
#include
using namespace std;
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to)
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
templateinline bool Max(T &a,T b){return ainline bool Min(T &a,T b){return b=0;--p)
upd(data(st[p].a-st[p].b*mid,0,st[p].from),m1,m2);
if(st[i].b*mid-st[i].a>(st[i].from==m1.from?m2.a:m1.a)) return 1;
}
return 0;
}
bool ck2(int u,LL mid){
int p=1;
data m1(inf,-1,-1),m2(inf,-1,-1);
for(int i=tp;i;--i){
for(;p<=tp&&st[p].a+st[i].a<0;++p)
upd(data(-st[p].b*mid-st[p].a,0,st[p].from),m1,m2);
if(st[i].b*mid+st[i].a>(st[i].from==m1.from?m2.a:m1.a)) return 1;
}
return 0;
}
void dfs(int u){
vis[u]=1,st[tp=1]=data(0,0,0);
go(u)if(!vis[v]) {
d[v]=data(e[i].c,1,v);
getdep(v,u,v);
}
sort(st+1,st+1+tp);
LL l=1,r=ans;
while(l>1;
if(ck1(u,mid)||ck2(u,mid)) r=mid;
else l=mid+1;
}
Min(ans,l);
go(u)if(!vis[v])
rt=0,sn=son[v],getrt(v,u),dfs(rt);
}
int main(){
scanf("%d%lld",&n,&k);
rep(i,1,n-1){
int a,b;LL w;
scanf("%d%d%lld",&a,&b,&w);
Add(a,b,w-k);
Min(ans,abs(w-k)+1);
}
sn=n,g[rt=0]=0x3f3f3f3f,getrt(1,0),dfs(rt);
printf("%lld
",ans-1);
return 0;
}