『结题记录』P2680 运输计划
LCA +树上差分+二分
\(Description\)
给一棵 \(n\) 个结点的带权树。可以将任意一条边的代价清零,使 \(m\) 条路径的最大值最小。
\(Solution\)
求最大值最小,考虑二分答案。
可以二分路径长度,求出所有大于当前路径长度的路径共同包含的边,找出这些边中边权最大的,判断最长路径减去这条边是否小于等于当前路径长度。
为什么要求出所有大于当前路径长度的路径共同包含的边?因为若一条路径减去最大边权小于等于当前长度,但改变不包含最大边权的边,则仍大于当前长度。
如何求重叠(共同包含)的边?可以使用树上差分来统计一条边被经过了几次。用一个变量 \(cnt\) 记录大于当前路径长度的路径个数,若 \(cnt=cf[x]\) 则该边是重叠的边。具体来说,设差分数组为 \(cf\) 先记录每条路径的起点,终点, LCA 。在遍历每个路径时,\(cf[起点]++\) \(cf[终点]++\) \(cf[LCA]-=2\) 。(因为是边上差分,不是点上差分,所以不用将 LCA 的父节点的差分减一)最后用 dfs 将每条边的差分统计出来与 \(cnt\) 比较即可。
\(Code\)
#include <iostream>
#include <cstring>
#define int long long
#define frer freopen("in.in","r",stdin);
#define frew freopen("out.out","w",stdout);
using namespace std;
const int Max = 3e5+10;
int n,m,lg;
int dfn[Max],tot;
int dep[Max];
int cnt,cf[Max],maxEdge,maxcLen,edgeDis[Max];
int s[Max],t[Max],lca[Max],len[Max],maxLen;
//maxLen 为最大路径长,maxEdge 为重叠的最大边权,edgeDis 记录的为由 x 的父亲到 x 的边权
/*abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz*/
struct EDGE
{
int Head[Max*2],Next[Max*2],Ver[Max*2],Edge[Max*2],tot;
inline void add(int x,int y,int z){
Ver[++tot]=y,Edge[tot]=z,Next[tot]=Head[x],Head[x]=tot;
}
}E;
namespace st
{
int st[25][Max];
inline int Min(int x,int y){return dfn[x]<dfn[y]?x:y;}
inline void build(){
for(int j=1;j <= lg;j++)
for(int i = 1;i+(1<<j)-1 <= n;i++)
st[j][i]=Min(st[j-1][i],st[j-1][i+(1<<j-1)]);
}
inline int getMin(int l,int r){
int k=__lg(r-l+1);
return Min(st[k][l],st[k][r-(1<<k)+1]);
}
}
/*abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz*/
void dfs(int x,int fa)
{
dfn[x]=++tot;
st::st[0][dfn[x]]=fa;
for(int i = E.Head[x];i;i=E.Next[i]){
int y = E.Ver[i],z=E.Edge[i];
if(y==fa)continue;
dep[y]=dep[x]+z;
edgeDis[y]=z;
dfs(y,x);
}
}
inline int LCA(int x,int y)
{
if(x==y)return x;
x=dfn[x],y=dfn[y];
if(x>y)swap(x,y);
return st::getMin(x+1,y);
}
void makeCf(int x,int fa){//统计
for(int i = E.Head[x];i;i=E.Next[i]){
int y=E.Ver[i];
if(y==fa)continue;
makeCf(y,x);
cf[x]+=cf[y];
}
if(cf[x]==cnt) maxEdge=max(maxEdge,edgeDis[x]);
return;
}
inline bool check(int x){//check
memset(cf,0,sizeof cf);
cnt=0;
maxcLen=0;
maxEdge=0;
for(int i = 1;i <= m;i++){
if(len[i]>x){
maxcLen=max(maxcLen,len[i]);
cnt++;
cf[s[i]]++;cf[t[i]]++;
cf[lca[i]]-=2;
}
}
makeCf(1,0);
if(maxcLen-maxEdge<=x)return 1;
return 0;
}
inline int dic(){//二分
int l=0,r=maxLen;
while(l < r){
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
return l;
}
inline int read(){
int num=0,fl=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') fl=-1;
c=getchar();
}
while(c >='0'&&c <='9'){
num=(num<<3)+(num<<1)+(c^48);
c=getchar();
}
return num*fl;
}
signed main(){
n=read(),m=read(),lg=__lg(n);
for(int i = 1;i < n;i++){
int x=read(),y=read(),z=read();
E.add(x,y,z);
E.add(y,x,z);
}
dfs(1,0);
st::build();
for(int i = 1;i <= m;i++){
s[i]=read(),t[i]=read();
lca[i]=LCA(s[i],t[i]);
len[i]=dep[s[i]]+dep[t[i]]-2*dep[lca[i]];
maxLen=max(maxLen,len[i]);
}
printf("%lld",dic());
return 0;
}