2018.09.26 bzoj4326: NOIP2015 运输计划(二分+树上差分)

本文介绍了一种使用树上差分和二分搜索解决特定树形结构问题的算法。通过将边权转化为点权,利用树上差分技巧维护路径上的操作,并结合二分搜索优化查询效率,实现复杂查询的快速响应。文章详细解释了算法流程,包括树状数组初始化、路径查询更新、最近公共祖先(LCA)查找等关键步骤。
摘要由CSDN通过智能技术生成

传送门
简单树上操作。
先转边权为点权。
显然所有的询问操作对应的路径会有一些交点,那么我们可以直接二分答案,对于所有大于二分值的询问用树上差分维护,最后dfs一遍每个点被覆盖了几次,当前情况合法当且仅当被覆盖次数与大于二分值的询问数相同且点权值可以使跟二分值相比差最大的询问合法。
代码:

#include<bits/stdc++.h>
#define N 300005
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,m,first[N],cnt=0,dis[N],fa[N],top[N],tim[N],dep[N],siz[N],hson[N],val[N],tot=0;
struct edge{int v,next,w;}e[N<<1];
struct Q{int u,v,t,w;}q[N<<1];
inline void add(int u,int v,int w){e[++cnt].v=v,e[cnt].next=first[u],e[cnt].w=w,first[u]=cnt;}
inline void dfs1(int p){
	siz[p]=1;
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==fa[p])continue;
		dis[v]=e[i].w+dis[p],val[v]=e[i].w,fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
}
inline void dfs2(int p,int tp){
	top[p]=tp;
	if(!hson[p])return;
	dfs2(hson[p],tp);
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v!=hson[p]&&v!=fa[p])dfs2(v,v);
	}
}
inline void dfs3(int p){
	for(int i=first[p];i;i=e[i].next){
		if(e[i].v==fa[p])continue;
		dfs3(e[i].v),tim[p]+=tim[e[i].v];
	}
}
inline void update(int i){++tim[q[i].u],++tim[q[i].v],tim[q[i].t]-=2;}
inline int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
inline bool check(int mid){
	int mx=0,pot=0;
	memset(tim,0,sizeof(tim));
	for(int i=1;i<=m;++i)if(q[i].w>mid)update(i),mx=max(mx,q[i].w-mid),++pot;
	dfs3(1);
	for(int i=1;i<=n;++i)if(tim[i]>=pot&&val[i]>=mx)return true;
	return false;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w),add(v,u,w);
	}
	dfs1(1),dfs2(1,1);
	int l=0,r=0,ans=0;
	for(int i=1;i<=m;++i){
		q[i].u=read(),q[i].v=read();
		q[i].t=lca(q[i].u,q[i].v),q[i].w=dis[q[i].u]+dis[q[i].v]-2*dis[q[i].t],r=max(r,q[i].w);
	}
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid))r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值