『结题记录』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;  
}
posted @ 2023-10-16 11:06  tkt  阅读(2)  评论(0编辑  收藏  举报