题目
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
1≤n,m≤3⋅105
思路
这跟天天爱跑步很像,都是同时出发…
我们可以预处理出
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
leni≥x 的所有路径都需要被减少,假设有
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
Max1−Max2<=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 遇到最大值最小这种问题先考虑二分答案