[bzoj4326][NOIP2015]运输计划

23 篇文章 0 订阅
15 篇文章 0 订阅

题目大意

树上有许多条路径,现在你可以将一条边的权值设为0,令所有路径的最大值最小。

最长路径瓶颈

我们注意到,设为0的边如果不在最长路径上,就不会减小答案。
因此我们可以找出最长路径,把原树转化为一条链+许多树的模型。
预处理出每个点i的from[i]表示由链上哪个点延伸的。
接下来我们可以枚举将链上哪条边变为0,则经过这条边的最长路径一定是最长链,我们只需维护出不经过这条边的最长路径。这个显然可以转化为添加删除问题,用set维护(n log n此题可能T,还有o(n)的方法也很简单,读者自行思考)。
lca应有tarjan打避免t的危险。

参考程序

#include<cstdio>
#include<cstring>
#include<cmath>
#include<set>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=300000+10;
struct dong{
    int x;
    friend bool operator <(dong a,dong b){
        return a.x>b.x;
    }
};
multiset<dong> t2;
int f[maxn][20],g[maxn],h[maxn],go[maxn*2],next[maxn*2],dis[maxn*2],u[maxn],v[maxn],w[maxn],fa[maxn];
int sum[maxn],num[maxn],a[maxn],d[maxn],dfn[maxn],nfd[maxn],fi[maxn],bh[maxn],zou[maxn],xia[maxn],from[maxn],dl[maxn];
int i,j,k,l,s,t,n,m,tot,ans,xdl;
bool bz[maxn];
dong suan;
int read(){
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void add(int x,int y,int z){
    go[++tot]=y;
    dis[tot]=z;
    next[tot]=h[x];
    h[x]=tot;
}
void add2(int x,int y,int z){
    zou[++tot]=y;
    bh[tot]=z;
    xia[tot]=fi[x];
    fi[x]=tot;
}
int getfa(int x){
    return fa[x]?fa[x]=getfa(fa[x]):x;
}
void dfs(int x,int y){
    d[x]=d[y]+1;
    f[x][0]=y;
    int t=h[x];
    while (t){
        if (go[t]!=y){
            g[go[t]]=dis[t];
            sum[go[t]]=sum[x]+dis[t];
            dfs(go[t],x);
        }
        t=next[t];
    }
}
void color(int x,int y){
    from[x]=y;
    int t=h[x];
    while (t){
        if (!from[go[t]]&&d[go[t]]==d[x]+1) color(go[t],y);
        t=next[t];
    }
}
void up(int x){
    if (!x) return;
    color(x,x);
    up(f[x][0]);
}
void tarjan(int x,int y){
    bz[x]=1;
    int t=h[x];
    while (t){
        if (go[t]!=y){
            tarjan(go[t],x);
            fa[go[t]]=x;
        }
        t=next[t];
    }
    t=fi[x];
    while (t){
        if (bz[zou[t]]){
            w[bh[t]]=getfa(zou[t]);
        }
        t=xia[t];
    }
}
void solve(int x){
    if (!x) return;
    int t=fi[x];
    while (t){
        if (!bh[t]){
            suan.x=a[zou[t]];
            t2.erase(suan);
        }
        t=xia[t];
    }
    t=fi[x];
    while (t){
        if (bh[t]){
            suan.x=a[zou[t]];
            t2.insert(suan);
        }
        t=xia[t];
    }
    //if (x==5) printf("%d\n",(*(t2.begin())).x);
    ans=min(ans,max(xdl-g[x],(*(t2.begin())).x));
    solve(f[x][0]);
}
int main(){
    freopen("transport.in","r",stdin);freopen("transport.out","w",stdout);
    n=read();m=read();
    fo(i,1,n-1){
        j=read();k=read();t=read();
        add(j,k,t);add(k,j,t);
    }
    tot=0;
    dfs(1,0);
    tot=0;
    fo(i,1,m){
        u[i]=read();v[i]=read();
        add2(u[i],v[i],i);
        add2(v[i],u[i],i);
    }
    tarjan(1,0);
    xdl=0;
    fo(i,1,m){
        a[i]=sum[u[i]]+sum[v[i]]-sum[w[i]]*2;
        if (a[i]>xdl){
            xdl=a[i];
            s=u[i];
            t=v[i];
        }
    }
    d[0]=0;
    dfs(s,0);
    up(t);
    tot=0;
    fill(fi+1,fi+n+1,0);
    fo(i,1,m){
        j=u[i];k=v[i];
        if (d[from[j]]>d[from[k]]) swap(j,k);
        add2(from[k],i,0);add2(from[j],i,1);
    }
    ans=1000000000;
    fo(i,1,m){
        suan.x=a[i];
        t2.insert(suan);
    }
    solve(t);
    printf("%d\n",ans);
    fclose(stdin);fclose(stdout);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值