[洛谷P2680]运输计划:二分答案+树链剖分+树上差分

分析:

本题相当于询问最大值的最小值,显然二分答案。
先求出所有路径的长度和LCA,这个可以通过树剖实现(将边的信息下放给孩子节点)。
在check(mid)时,将所有长度大于mid的路径(不合法路径)在树上标记,即tag[u]+=1,tag[v]+=1,tag[lca(u,v)]-=2。
那么一条边被不合法路径包含的次数便是以这条边连接的两个结点中深度较深的那个结点为根的子树的tag之和。
显然只有当有一条边被不合法路径包含的次数等于不合法路径条数(即这条边被所有不合法路径包含),并且所有不合法路径中最长路径的长度-这条边的边权<=mid时,mid为一个合法的答案。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
inline LL read(){
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=300005;
int n,m,head[MAXN],ecnt;
int fa[MAXN],pc[MAXN],dep[MAXN],siz[MAXN],top[MAXN],id[MAXN],w[MAXN],tot;
int sum[MAXN],tag[MAXN];
struct Edge{
    int to,nxt,w;
}e[MAXN<<1];
inline void add_edge(int bg,int ed,int val){
    ecnt++;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    e[ecnt].w=val;
    head[bg]=ecnt;
}
struct Path{
    int u,v,lca,w;
}pa[MAXN];
void dfs1(int x,int pre,int depth){
    fa[x]=pre;
    dep[x]=depth;
    siz[x]=1;
    int maxsiz=-1;
    for(int i=head[x];i;i=e[i].nxt){
        int ver=e[i].to;
        if(ver==pre) continue;
        w[ver]=e[i].w;
        dfs1(ver,x,depth+1);
        siz[x]+=siz[ver];
        if(siz[ver]>maxsiz) maxsiz=siz[ver],pc[x]=ver;
    }
}
void dfs2(int x,int topf){
    top[x]=topf;
    id[x]=++tot;
    sum[tot]=w[x];
    if(!pc[x]) return;
    dfs2(pc[x],topf);
    for(int i=head[x];i;i=e[i].nxt){
        int ver=e[i].to;
        if(ver==fa[x]||ver==pc[x]) continue;
        dfs2(ver,ver);
    }
}
inline void buildSum(){
    for(int i=2;i<=n;i++)
        sum[i]+=sum[i-1];
}
inline pair<int,int> pathquery(int x,int y){
    pair<int,int> ret(0,0);
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ret.first+=sum[id[x]]-sum[id[top[x]]-1];
        x=fa[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    ret.second=x;
    if(dep[x]==dep[y]) return ret;
    ret.first+=sum[id[y]]-sum[id[x]];
    return ret;
}
void dfs3(int x){
    for(int i=head[x];i;i=e[i].nxt){
        int ver=e[i].to;
        if(ver==fa[x]) continue;
        dfs3(ver);
        tag[x]+=tag[ver];
    }
}
inline bool check(int mid){
    memset(tag,0,sizeof tag);
    int inva=0,mx=-1;
    for(int i=1;i<=m;i++){
        if(pa[i].w<=mid) continue;
        tag[pa[i].u]+=1;
        tag[pa[i].v]+=1;
        tag[pa[i].lca]-=2;
        mx=max(mx,pa[i].w);
        inva++;
    }
    if(mx==-1) return 1;
    dfs3(1);
    for(int i=1;i<=n;i++){
        if(tag[i]!=inva) continue;
        if(mx-w[i]>mid) continue;
        return 1;
    }
    return 0;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
    dfs1(1,0,1);
    dfs2(1,1);
    buildSum();
    for(int i=1;i<=m;i++){
        pa[i].u=read(),pa[i].v=read();
        pair<int,int> temp=pathquery(pa[i].u,pa[i].v);
        pa[i].lca=temp.second;
        pa[i].w=temp.first;
    }
    int l=0,r=(n-1)*1000,ans;
    while(l<=r){
        int mid=((l+r)>>1);
        if(check(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9609931.html