UOJ
把所有边权都减去 k k k ,那么就要求平均边权的绝对值最小。
由于结果要向下取整,因此我们判定的符号应该是严格小于,即 − m i d < a n s < m i d -mid < ans < mid −mid<ans<mid
考虑点分治,把一条路径表示为 ( A i , B i ) (A_i,B_i) (Ai,Bi),其中 A i A_i Ai 表示根到节点的边权和, B i B_i Bi 表示深度,那么我们如果要合并两条路径就需要
− m i d < A 1 + A 2 B 1 + B 2 < m i d -mid<\frac {A_1+A_2} {B_1+B_2}<mid −mid<B1+B2A1+A2<mid
不难发现分母是大于 0 0 0 的,因此我们可以对分子的正负性进行讨论,当我们保证分子为正时, A 1 + A 2 < m i d B 1 + m i d B 2 A_1+A_2<midB_1+midB_2 A1+A2<midB1+midB2,那么 A 1 − m i d B 1 < m i d B 2 − A 2 A_1-midB_1<midB_2-A_2 A1−midB1<midB2−A2。对 A A A 排序后即可two-pointer,记录一个最小值即可,同时由于拼接的路径不能在一棵子树中,我们需要记录第二小的另一棵子树中的答案。分子为负时同理。
每次分治时可以把二分的上界不断更新,所以把二分写在点分治里面会稍微快一些,而且也不需要预处理什么鬼点分树了。
时间复杂度 O ( n log n log v ) O(n\log n\log v) O(nlognlogv)。注意 I N F INF INF 不要设小了。
#include
#include
using namespace std;
typedef long long ll;
const int maxn=50010;
const ll INF=1e18;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
struct data{
int v,nxt;ll w;
data(const int _v=0,const ll _w=0ll,const int _nxt=0){v=_v;w=_w;nxt=_nxt;}
}edge[maxn<<1];
struct node{
ll sum;int dep,anc;
node(const ll _sum=0ll,const int _dep=0,const int _anc=0){sum=_sum;dep=_dep;anc=_anc;}
bool operator < (const node &b)const{return sum<b.sum;}
}m1,m2,stk[maxn];
int n,p,top,head[maxn];
int rt,sn,mx[maxn],sz[maxn],vis[maxn];
ll m,ans=INF;
void insert(int u,int v,ll w)
{
edge[++p]=data(v,w,head[u]);head[u]=p;
edge[++p]=data(u,w,head[v]);head[v]=p;
}
void getrt(int x,int pre)
{
sz[x]=1;mx[x]=0;
for(int i=head[x];i;i=edge[i].nxt)
if(!vis[edge[i].v]&&edge[i].v^pre)
{
getrt(edge[i].v,x);
sz[x]+=sz[edge[i].v];
getmax(mx[x],sz[edge[i].v]);
}
getmax(mx[x],sn-sz[x]);
if(mx[x]<mx[rt]) rt=x;
}
void getdis(int x,int pre,ll sum,int dep,int anc)
{
stk[++top]=node(sum,dep,anc);
for(int i=head[x];i;i=edge[i].nxt)
if(!vis[edge[i].v]&&edge[i].v^pre)
getdis(edge[i].v,x,sum+edge[i].w,dep+1,anc);
}
void input()
{
int u,v;ll w;
read(n);read(m);
for(int i=1;i<n;i++)
{
read(u);read(v);read(w);
insert(u,v,w-m);
getmin(ans,abs(w-m));
}
mx[0]=0x3f3f3f3f;rt=0;sn=n;
getrt(1,1);
}
void update(node now,ll mid)
{
if(now.sum<m1.sum)
{
if(now.anc==m1.anc){m1.sum=now.sum;return ;}
m2=m1;m1=now;return ;
}
if(now.sum<m2.sum&&now.anc!=m1.anc) m2=now;
}
int check1(ll mid)
{
int p=top;
m1=m2=node(INF,-1,-1);
for(int i=1;i<=top;i++)
{
for(;p&&stk[p].sum+stk[i].sum>=0;--p)
update(node(stk[p].sum-stk[p].dep*mid,-1,stk[p].anc),mid);
if(stk[i].dep*mid-stk[i].sum>(stk[i].anc==m1.anc?m2.sum:m1.sum))
return 1;
}
return 0;
}
int check2(ll mid)
{
int p=1;
m1=m2=node(INF,-1,-1);
for(int i=top;i;i--)
{
for(;p<=top&&stk[p].sum+stk[i].sum<0;++p)
update(node(-stk[p].sum-stk[p].dep*mid,-1,stk[p].anc),mid);
if(stk[i].dep*mid+stk[i].sum>(stk[i].anc==m1.anc?m2.sum:m1.sum))
return 1;
}
return 0;
}
void dfs(int x)
{
vis[x]=1;getrt(x,0);
stk[top=1]=node(0,0,x);
for(int i=head[x];i;i=edge[i].nxt)
if(!vis[edge[i].v])
getdis(edge[i].v,x,edge[i].w,1,edge[i].v);
sort(stk+1,stk+top+1);
ll L=1,R=ans,mid;
while(L<=R)
{
mid=(L+R)>>1;
if(check1(mid)||check2(mid)) ans=mid-1,R=mid-1;//ans
else L=mid+1;
}
for(int i=head[x];i;i=edge[i].nxt)
if(!vis[edge[i].v])
{
rt=0;sn=sz[edge[i].v];
getrt(edge[i].v,x);
dfs(rt);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
input();
dfs(rt);
printf("%lld\n",ans);
return 0;
}