运输计划[NOIP-Day2-T3](二分,树上差分)

题目

Luogu
给你一个 n n n 个点的树,每条边 ( u i , v i ) (u_i,v_i) (ui,vi) 通过时间为 t i t_i ti ,现在有 m m m 个订单同时运送,第 i i i 个订单从 s i s_i si 出发到 t i t_i ti m m m 个订单同时出发,你现在可以让一条边通过时间为 0 0 0 ,求最大运输时间最小值?
1 ≤ n , m ≤ 3 ⋅ 1 0 5 1\le n,m\le 3\cdot 10^5 1n,m3105

思路

这跟天天爱跑步很像,都是同时出发…
我们可以预处理出 s i s_i si t i t_i ti 的长度 l e n i len_i leni
最大值最小,考虑二分最大值 x x x
那么对于 l e n i ≥ x len_i\ge x lenix 的所有路径都需要被减少,假设有 c n t cnt cnt
但是你发现你只有一次修改的权利
于是考虑边差,就是找出覆盖次数等于 c n t cnt cnt 的最大的边权 M a x 2 Max_2 Max2
再找出 c n t cnt cnt 条中长度最大的
只需要满足 M a x 1 − M a x 2 < = x Max_1-Max_2<=x Max1Max2<=x 即可


这道题似乎倍增被卡了…,以后还是写树剖吧…

代码

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<cstdio>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 300000
#define mp make_pair
#define INF 0x3f3f3f3f
struct Edge{
	int v,w,id,nxt;
}edge[2*MAXN+5];
int ecnt,head[MAXN+5];
void Addedge(int u,int v,int w,int id){
	edge[++ecnt]=(Edge){v,w,id,head[u]},head[u]=ecnt;
	edge[++ecnt]=(Edge){u,w,id,head[v]},head[v]=ecnt;
	return ;
}
int n,m,siz[MAXN+5],dep[MAXN+5],fa[MAXN+5];
int len[MAXN+5],fd[MAXN+5],Maxson[MAXN+5],Tp[MAXN+5];
void DFS1(int u){
	siz[u]=1;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v,w=edge[i].w,id=edge[i].id;
		if(v==fa[u]) continue;
		len[v]=len[u]+w;
		fa[v]=u,fd[v]=id,dep[v]=dep[u]+1;
		DFS1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[Maxson[u]])
			Maxson[u]=v;
	}
	return ;
}
void DFS3(int u,int tp){
	Tp[u]=tp;
	if(!Maxson[u])
		return ;
	DFS3(Maxson[u],tp);
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
		if(v==fa[u]||v==Maxson[u]) continue;
		DFS3(v,v);
	}
	return ;
}
int LCA(int u,int v){
	while(Tp[u]!=Tp[v]){
		if(dep[Tp[u]]<=dep[Tp[v]])
			v=fa[Tp[v]];
		else u=fa[Tp[u]];
	}
	return dep[u]<dep[v]?u:v;
}
int U[MAXN+5],V[MAXN+5],W[MAXN+5],cha[MAXN+5];
int s[MAXN+5],t[MAXN+5],Lca[MAXN+5],Dis[MAXN+5];
inline int Max(register int a,register int b){return a>b?a:b;}
void DFS2(int u){
	for(register int i=head[u];i;i=edge[i].nxt){
		register int v=edge[i].v,id=edge[i].id;
		if(v==fa[u]) continue;
		DFS2(v),cha[fd[u]]+=cha[id];
	}
	return ;
}
bool check(int x){
	memset(cha,0,sizeof(cha));
	register int cnt=0,Max1=0,Max2=0;
	for(register int i=1;i<=m;i++)
		if(Dis[i]>x){
			cnt++;
			Max1=max(Max1,Dis[i]);
			cha[fd[s[i]]]++,cha[fd[t[i]]]++;
			cha[fd[Lca[i]]]-=2;
		}
	if(Max1<=x)
		return 1;
	DFS2(1),cha[0]=0;
	for(register int i=1;i<=n;i++)
		if(cha[i]==cnt)
			Max2=Max(Max2,W[i]);
	return Max1-Max2<=x;
}
int main(){
	n=read(),m=read();
	for(register int i=1;i<n;i++){
		U[i]=read(),V[i]=read(),W[i]=read();
		Addedge(U[i],V[i],W[i],i);
	}
	DFS1(1);
	DFS3(1,1);
	for(register int i=1;i<=m;i++){
		s[i]=read(),t[i]=read();
		Lca[i]=LCA(s[i],t[i]);
		Dis[i]=len[s[i]]+len[t[i]]-2*len[Lca[i]];
	}
	register int L=-1,R=300000000;
	while(L+1<R){
		register int Mid=(L+R)>>1;
		if(check(Mid)) R=Mid;
		else L=Mid;
	}
	printf("%d\n",R);
	return 0;
}

思考

C S P CSP CSP 遇到最大值最小这种问题先考虑二分答案

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值