NOIP 2015 运输计划

NOIP 2015 运输计划

题目描述

给定一棵树,树有边权。现有\(m\)条路径,求把一条边权变为0后,最大的路径长度的最小值。

解题思路

首先考虑\(O(nm)\)的暴力,枚举每条边,然后暴力check一下最长的路径。

思考怎么优化这个暴力。一个很明显的地方是,如果我们不把最长路径上的某条边变为0,那么答案不会变化。这样我们要枚举最长路径上的边,删去之后找最大值。

经过这条边的路径的最大值肯定就是枚举的路径长度-边长。考虑求不经过的。求不经过某条边的路径长度最大值。

一条树上路径可以树链剖分,在树上被剖成log个dfs序区间\([l_1,r_1] [l_2,r_2]...[l_k,r_k]\)。没被这个路径覆盖的边:\([1,l_1 -1], [r_1 + 1, l_2 - 1]...[r_k+1, n]\). 区间总数还是\(O(logn)\)个的。对每一条路径这样再用线段树维护一下,复杂度\(O(mlog^2n)\)

不用数据结构的求法:\(e= (u, v)\)。当u是父亲,不经过e的路径在v的子树里。剩余的路径:把树倒过来,当v是父亲,u的子树的路径。从最长路径的两个端点做dp,求子树最长路径。

注意\(u=v\)的路径需要特判。

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 11;
int n, m;
int head[N], nex[N<<1], to[N<<1], wei[N<<1], size;
int dis[N], dep[N], fa[N];
int son[N], sz[N], dfn[N], top[N], cnt;
struct Path{
    int u, v, w;
}E[N];
struct seg{
    int l, r;
    bool operator < (const seg &a)const{
        return l < a.l;
    }
}a[N], b[N];
struct Seg{
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    int t[N<<2], tag[N<<2];
    void add_tag(int p, int v){
        t[p] = max(t[p], v);
        tag[p] = max(tag[p], v);
    }
    void push_down(int p){
        if(!tag[p])return ;
        add_tag(ls, tag[p]);
        add_tag(rs, tag[p]);
        tag[p] = 0;
    }
    void modi(int p, int l, int r, int x, int y, int v){
        if(l > y || r < x)return ;
        if(l >= x && r <= y){
            add_tag(p, v);
            return ;
        }
        push_down(p);
        int mid = l + r >> 1;
        modi(ls, l, mid, x, y, v);
        modi(rs, mid + 1, r, x, y, v);
        t[p] = max(t[ls], t[rs]);
    }
    int query(int p, int l, int r, int x){
        if(l == r){
            return t[p];
        }
        int mid = l + r >> 1;
        push_down(p);
        if(mid >= x)return query(ls, l, mid, x);
        else return query(rs, mid + 1, r, x);
    }
}T;
int read(){
    int x = 0;
    char ch = getchar();
    while(ch > '9' || ch < '0')ch = getchar();
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - 48, ch = getchar();
    return x;
}
bool cmp(Path a, Path b){
    return a.w > b.w;
}
void add(int x, int y, int z){
    to[++size] = y;
    nex[size] = head[x];
    head[x] = size;
    wei[size] = z;
}
void dfs(int u){
    sz[u] = 1;
    dep[u] = dep[fa[u]] + 1;
    for(int i = head[u];i;i = nex[i]){
        int v = to[i];
        if(v == fa[u])continue;
        fa[v] = u;
        dis[v] = dis[u] + wei[i];
        dfs(v);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]])son[u] = v;
    }
}
void dfs(int u, int tp){
    top[u] = tp;
    dfn[u] = ++cnt;
    if(son[u])dfs(son[u], tp);
    for(int i = head[u];i;i = nex[i]){
        int v = to[i];
        if(v == fa[u] || v == son[u])continue;
        dfs(v, v);
    }
}
int lca(int u, int v){
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]])swap(u, v);
        u = fa[top[u]];
    }
    if(dep[u] > dep[v])return v;
    else return u;
}
void get_cover(int u, int v, int w){
    int cnta = 0, cntb = 0;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]])swap(u, v);
        a[++cnta] = (seg){dfn[top[u]], dfn[u]};
        u = fa[top[u]];
    }
    if(u != v){
        if(dep[u] > dep[v])swap(u, v);
        a[++cnta] = (seg){dfn[u] + 1, dfn[v]};
    }
    sort(a + 1, a + 1 + cnta);
    int p = 2;
    for(int i = 1;i <= cnta; i++){
        if(a[i].l != p){
            b[++cntb] = (seg){p, a[i].l - 1};
        }
        p = a[i].r + 1;
    }
    if(p <= n)b[++cntb] = (seg){p, n};
    for(int i = 1;i <= cntb; i++){
        T.modi(1, 2, n, b[i].l, b[i].r, w);
    }
}
int main(){
    cin>>n>>m;
    int u, v, w;
    for(int i = 1;i < n; i++){
        u = read(); v = read(); w = read();
        add(u, v, w); add(v, u, w);
    }
    dfs(1);
    dfs(1, 1);
    for(int i = 1;i <= m; i++){
        u = read(); v = read();
        w = dis[u] + dis[v] - 2 * dis[lca(u, v)];
        E[i] = (Path){u, v, w};
        //printf("i=%d u=%d v=%d w=%d\n", i, u, v, w);
        get_cover(u, v, w);
    }
    sort(E + 1, E + 1 + m, cmp);
    u = E[1].u, v = E[1].v;
    int ans = 1e9;
    if(u == v)ans = 0;
    while(u != v){
        if(dep[v] > dep[u])swap(u, v);
        ans = min(ans, max(E[1].w - (u == v ? 0 : (dis[u] - dis[fa[u]])), T.query(1, 2, n, dfn[u])));
        u = fa[u];
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2020-08-06 10:51  LawrenceD  阅读(93)  评论(0)    收藏  举报