[ybtoj 4.5.5/P2680] 运输计划 [LCA/二分]

该博客主要介绍了如何利用树上差分和二分查找解决一类特殊的最优化问题:在树形结构中,寻找将一条边的权重设为0后,所有路径权值的最大值最小。文章通过建立数学模型,阐述了二分查找的合法条件,并展示了如何判断条件是否满足。同时,详细解释了树上差分在计算路径经过边的次数中的应用,以及LCA(最近公共祖先)的计算方法。最后,给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

题意

一棵树,有 N N N 个点,有边权
给你 M M M 条路径,每条路径的权值等于在这条路径上的所有边的边权之和
求在将一条边的边权改成 0 0 0 的情况下,所有的路径的权值的最大值最小

思路

在看到这种“最大值最小的”题目时,有一个常见的思路就是二分

考虑直接二分答案

然后就可以得到一个很显然的结论

一个答案是合法的,当且仅当所有权值比答案大的路径都经过同一条边且所有的路径,且这些路径减去这条边的权值都比答案小

然后就可以把这道题的判断改为判定如上条件了

在判定每一条边被路径经过的次数时候可以利用树上差分的方法

就差不多结束了

代码

#include<bits/stdc++.h>
#define N 300010
using namespace std;
struct qq{
	int to,from,next,dis;
}a[N*2];
struct pp{
	int x,y,lca,dis;
}b[N];
int f[N][20],dis[N],dep[N],s[N],head[N],n,m,tot,ans;
void add(int x,int y,int z){a[++tot].to=y;a[tot].from=x;a[tot].dis=z;a[tot].next=head[x];head[x]=tot;}
bool o(pp x,pp y){return x.dis>y.dis;}
void dfs(int x,int fa,int dist)
{
	f[x][0]=fa;dep[x]=dep[fa]+1;dis[x]=dis[fa]+dist;
	for(int i=1;i<=18;i++)f[x][i]=f[f[x][i-1]][i-1];
	for(int i=head[x];i;i=a[i].next)
		if(a[i].to!=fa)dfs(a[i].to,x,a[i].dis);
}
void dfs(int x,int fa)
{
	for(int i=head[x];i;i=a[i].next)
		if(a[i].to!=fa)dfs(a[i].to,x),s[x]+=s[a[i].to];
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])x^=y^=x^=y;
	for(int i=19;i>=0;i--)
		if(dep[f[x][i]]>=dep[y])x=f[x][i];
	if(x==y)return x;
	for(int i=19;i>=0;i--)
		if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}
bool check(int mid)
{
	int x=0,mx=0;
	for(int i=1;i<=n;i++)s[i]=0;
	for(int i=1;i<=m&&b[i].dis>mid;i++)
		s[b[i].x]++,s[b[i].y]++,s[b[i].lca]--,s[f[b[i].lca][0]]--,x=i;
	dfs(1,0);
	for(int i=1;i<2*n;i+=2)
		if(s[a[i].to]==x&&s[a[i].from]==x)mx=max(mx,a[i].dis);
	return b[1].dis<=mid+mx;
}
int main()
{
	int x,y,z,l=0,r=0,mid;
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);add(y,x,z);
	}
	dfs(1,0,0);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		b[i].x=x;b[i].y=y;b[i].lca=lca(x,y);
		b[i].dis=dis[x]+dis[y]-2*dis[b[i].lca];
	}
	sort(b+1,b+m+1,o);
	r=b[1].dis;
	while(l<=r)
	{
		mid=l+r>>1;
		if(check(mid))ans=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值