P6670 [清华集训2016] 汽水

题目

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;
}
posted @ 2021-05-06 23:25  __Anchor  阅读(52)  评论(0编辑  收藏  举报