1. 程式人生 > 實用技巧 >NOIP 2015 運輸計劃

NOIP 2015 運輸計劃

NOIP 2015 運輸計劃

題目描述

給定一棵樹,樹右邊權。現有\(m\)條路徑,求把一條邊權變為0後,最大的路徑長度的最小值。

解題思路

首先考慮\(O(nm)\)的暴力,列舉每條邊,然後暴力check一下最長的路徑。

思考怎麼優化這個暴力。一個很明顯的地方是,如果我們不把最長路徑上的某條邊變為0,那麼答案不會變化。這樣我們要列舉最長路徑上的邊,刪去之後找最大值。

經過這條邊的路徑的最大值肯定就是列舉的路徑長度-邊長。考慮求不經過的。

一條樹上路徑可以樹鏈剖分,在樹上被剖成log個dfs序區間。這包含所有經過的路徑。我們把一個點的父邊放在該點上,這樣我們對這log個區間求個補集,就是沒有經過的邊的區間,總數還是\(O(logn)\)

個的。這樣再用線段樹維護一下,就可以了。

注意\(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;
}