[BZOJ] 4326: NOIP2015 运输计划

二分答案,把原问题转化为\(log\)个判定问题

现在问题是,给出一个答案\(t\),问是否可行

我们找出\(len>t\)的所有路径,这些路径都要被变短,也就要找到这些路径的一条最长公共边

如何找最长公共边?先考虑公共边,可以用树上差分解决,最长就顺便取max即可

//Stay foolish,stay hungry,stay young,stay simple
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
using namespace std;

inline int rd(){
    int ret=0;char c;
    while(c=getchar(),!isdigit(c));
    while(isdigit(c))ret=ret*10+c-'0',c=getchar();
    return ret;
}

const int MAXN =300005,M=300005;
int n,m;
struct Edge{
    int nex,to,w;
}e[M<<1];
int ecnt,head[MAXN];
inline void add(const int x,const int y,const int w){
    e[++ecnt].nex = head[x];
    e[ecnt].to = y;
    e[ecnt].w = w;
    head[x] = ecnt;
}
int fa[MAXN],hs[MAXN],dep[MAXN];
int dfs1(int x,int pre,int w){
    fa[x]=pre;dep[x]=dep[pre]+w;
    int mx=0,tmp,siz=1;
    for(register int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v==pre) continue;
        tmp=dfs1(v,x,e[i].w);siz+=tmp;
        if(tmp>mx){mx=tmp;hs[x]=v;}
    }
    return siz;
}
int top[MAXN];
void dfs2(int x,int tp){
    top[x]=tp;
    if(hs[x]) dfs2(hs[x],tp);
    for(register int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v==fa[x]||v==hs[x]) continue;
        dfs2(v,v);  
    }
}
int lca(int x,int y){
    while(top[x]!=top[y])
        dep[top[x]]<dep[top[y]]?y=fa[top[y]]:x=fa[top[x]];
    return dep[x]<dep[y]?x:y;
}

struct Links{
    int x,y,l,len;
}lk[MAXN];
int num,mx,val[MAXN];
int d[MAXN];

void gfs(int x){
    for(register int i=head[x];i;i=e[i].nex){
        int v=e[i].to;
        if(v==fa[x]) continue;
        gfs(v);
        d[x]+=d[v];
    }
    if(d[x]==num){
        mx=max(mx,dep[x]-dep[fa[x]]);   
    }
}
bool check(int t){
    num=0;mx=0;
    memset(d,0,sizeof(d));
    int mxl=0;
    for(register int i=1;i<=m;i++){
        if(lk[i].len>t){
            num++;  
            d[lk[i].x]++;
            d[lk[i].y]++;
            d[lk[i].l]-=2;
            mxl=max(mxl,lk[i].len);
        }
        
    }
    gfs(1);
    return (mxl-mx<=t);
}

signed main(){
    n=rd();m=rd();
    int x,y,w;
    for(register int i=1;i<=n-1;i++){
        x=rd();y=rd();w=rd();
        add(x,y,w);add(y,x,w);
    }
    dfs1(1,0,0);dfs2(1,1);
    for(register int i=1;i<=m;i++){
        x=lk[i].x=rd();
        y=lk[i].y=rd();
        int l=lca(x,y);
        lk[i].l=l;
        lk[i].len=dep[x]+dep[y]-(dep[l]<<1);
    }
    int l=0,r=3e8,mid,ans;
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans;
    return 0;
}

转载于:https://www.cnblogs.com/ghostcai/p/9781607.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值