P6670 [清华集训2016] 汽水
题目
给一棵树,边有边权,要求找到一条路径使得其平均值和 \(k\) 最接近。
分析
首先树上路径容易想到点分治。
然后发现这可以套一个 0/1 分数规划,于是我们可以把所有的边权减掉 \(k\),再二分 \(mid\)。
现在的问题就是判断了。
我们发现答案具有单调性,于是我们可以排序过后双指针维护路径。
题解
代码
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;bool f=false;char ch=getchar();
while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
const int N=2e5+5;
const ll INF=1e18+7;
ll Ans,k;
int n;
int head[N],nex[N],to[N],siz[N],idx;
ll val[N];
bool vis[N];
int Size,Root,FMax;
inline void add(int u,int v,ll w){
nex[++idx]=head[u];
to[idx]=v;
val[idx]=w;
head[u]=idx;
return ;
}
void GetRoot(int x,int fa){
siz[x]=1;int Max=0;
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(vis[y]||y==fa) continue;
GetRoot(y,x);siz[x]+=siz[y];
Max=max(Max,siz[y]);
}
Max=max(Max,Size-siz[x]);
if(Max<=FMax) FMax=Max,Root=x;
return ;
}
struct Edge{
ll sum;int num,bel;
Edge(ll sum=0,int num=0,int bel=0):sum(sum),num(num),bel(bel){}
inline bool operator < (const Edge &B)const{return sum<B.sum;}
}p[N],M1,M2;
int Cnt;
void GetPath(int x,int fa,ll W,int D,int bel){
p[++Cnt]=Edge(W,D,bel);
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(y==fa||vis[y]) continue;
GetPath(y,x,W+val[i],D+1,bel);
}
return ;
}
inline void Modify(Edge now,ll mid){
if(now.sum<M1.sum){
if(now.bel==M1.bel) return M1=now,void();
return M2=M1,M1=now,void();
}
if(now.sum<M2.sum&& now.bel^M1.bel) M2=now;
return ;
}
inline bool Check1(ll mid){
int j=Cnt;
M1=M2=Edge(INF,-1,-1);
for(int i=1;i<=Cnt;i++){
for(;j&&p[i].sum+p[j].sum>=0;j--) Modify(Edge(p[j].sum-p[j].num*mid,-1,p[j].bel),mid);
if(p[i].num*mid-p[i].sum>(p[i].bel==M1.bel?M2.sum:M1.sum)) return true;
}
return false;
}
inline bool Check2(ll mid){
int j=1;
M1=M2=Edge(INF,-1,-1);
for(int i=Cnt;i>=1;i--){
for(;j<=Cnt&&p[i].sum+p[j].sum<0;j++) Modify(Edge(-p[j].sum-p[j].num*mid,-1,p[j].bel),mid);
if(p[i].num*mid+p[i].sum>(p[i].bel==M1.bel?M2.sum:M1.sum)) return true;
}
return false;
}
void DFS(int x){
vis[x]=true;Cnt=0;
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(vis[y]) continue;
GetPath(y,x,val[i],1,y);
}
sort(p+1,p+Cnt+1);
ll l=1,r=Ans;
while(l<=r){
ll mid=l+r>>1;
if(Check1(mid)||Check2(mid)) Ans=mid-1,r=mid-1;
else l=mid+1;
}
for(int i=head[x];i;i=nex[i]){
int y=to[i];
if(vis[y]) continue;
Root=y,Size=FMax=siz[y],GetRoot(y,x);DFS(Root);
}
return ;
}
int main(){
read(n),read(k);Ans=INF;
for(int i=1;i<n;i++){
int u,v;ll w;
read(u),read(v),read(w);
add(u,v,w-k),add(v,u,w-k);
Ans=min(Ans,1ll*abs(w-k));
}
Root=1,FMax=Size=n;GetRoot(1,0),DFS(Root);
write(Ans);
return 0;
}