zoukankan      html  css  js  c++  java
  • BZOJ 4326 运输计划

    二分答案+树链剖分+树上差分

    我们假设x是最小的花费,可以想到给定x,所有运输计划中花费大于x的计划必须经过虫洞,且最长的一条的花费减去虫洞所在边的花费要小于等于x
    那么对于x,虫洞所在的位置肯定是确定的,假设x可以取更小,那么就没有一个合法方案可以放虫洞,x取更大,显然该方案也合法,这是一个明显符合单调性的问题,我们可以用二分答案求解。
    其实最大值最小就是答案具有单调性的特征啦。。

    通过上述分析,我们可以确定虫洞所在位置就是花费大于x的运输计划的交,即该边被覆盖次数等于花费大于x的运输计划数,那么对于每个x,我们可以用树上边差分的方式来求出交。
    如何求最大的花费呢?我们考虑差分时要求LCA,且要求出树上的路径,那么树链剖分就再好不过了!
    在dfs1的时候我们就需要吧边权塞给点(便是该点到其父亲的权值),然后通过树链剖分把LCA,和两点花费求出来。最后用max维护最大即可。

    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    #define full(a, b) memset(a, b, sizeof a)
    using namespace std;
    typedef long long ll;
    inline int lowbit(int x){ return x & (-x); }
    inline int read(){
        int X = 0, w = 0; char ch = 0;
        while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }
        while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
        return w ? -X : X;
    }
    inline int gcd(int a, int b){ return a % b ? gcd(b, a % b) : b; }
    inline int lcm(int a, int b){ return a / gcd(a, b) * b; }
    template<typename T>
    inline T max(T x, T y, T z){ return max(max(x, y), z); }
    template<typename T>
    inline T min(T x, T y, T z){ return min(min(x, y), z); }
    template<typename A, typename B, typename C>
    inline A fpow(A x, B p, C lyd){
        A ans = 1;
        for(; p; p >>= 1, x = 1LL * x * x % lyd)if(p & 1)ans = 1LL * x * ans % lyd;
        return ans;
    }
    const int N = 300005;
    int n, m, cnt, dfn, head[N], size[N], p[N], son[N], depth[N], top[N], w[N], val[N], id[N], tmp[N];
    int tree[N<<2], num, len, ml, ans;
    struct Edge { int v, next, w; } edge[N<<1];
    struct Ask {
        int u, v, lca, dis;
        bool operator < (const Ask &rhs) const {
            return dis > rhs.dis;
        }
    } ask[N<<1];
    
    void addEdge(int a, int b, int w){
        edge[cnt].v = b, edge[cnt].w = w, edge[cnt].next = head[a], head[a] = cnt ++;
    }
    
    void dfs1(int s, int fa){
        depth[s] = depth[fa] + 1;
        p[s] = fa;
        size[s] = 1;
        int child = -1;
        for(int i = head[s]; i != -1; i = edge[i].next){
            int u = edge[i].v;
            if(u == fa) continue;
            dfs1(u, s);
            size[s] += size[u], val[u] = edge[i].w;
            if(size[u] > child) child = size[u], son[s] = u;
        }
    }
    
    void dfs2(int s, int tp){
        id[s] = ++dfn;
        w[id[s]] = val[s];
        top[s] = tp;
        if(son[s] != -1) dfs2(son[s], tp);
        for(int i = head[s]; i != -1; i = edge[i].next){
            int u = edge[i].v;
            if(u == p[s] || u == son[s]) continue;
            dfs2(u, u);
        }
    }
    
    void push_up(int rt){
        tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
    }
    
    void buildTree(int rt, int l, int r){
        if(l == r){
            tree[rt] = w[l];
            return;
        }
        int mid = (l + r) >> 1;
        buildTree(rt << 1, l, mid);
        buildTree(rt << 1 | 1, mid + 1, r);
        push_up(rt);
    }
    
    int query(int rt, int l, int r, int queryL, int queryR){
        if(queryL > queryR) return 0;
        if(l == queryL && r == queryR){
            return tree[rt];
        }
        int mid = (l + r) >> 1;
        if(queryL > mid) return query(rt << 1 | 1, mid + 1, r, queryL, queryR);
        else if(queryR <= mid) return query(rt << 1, l, mid, queryL, queryR);
        else return query(rt << 1, l, mid, queryL, mid) +
                    query(rt << 1 | 1, mid + 1, r, mid + 1, queryR);
    }
    
    void lca(int x, int y, int k){
        int v = 0;
        while(top[x] != top[y]){
            if(depth[top[x]] < depth[top[y]]) swap(x, y);
            v += query(1, 1, n, id[top[x]], id[x]);
            x = p[top[x]];
        }
        if(depth[x] > depth[y]) swap(x, y);
        v += query(1, 1, n, id[x] + 1, id[y]);
        ask[k].lca = x, ask[k].dis = v;
    }
    
    void dfs3(int s, int fa){
        for(int i = head[s]; i != -1; i = edge[i].next){
            int u = edge[i].v;
            if(u == fa) continue;
            dfs3(u, s);
            tmp[s] += tmp[u];
        }
        if(s != 1 && tmp[s] == num && val[s] > len) len = val[s];
    }
    
    bool check(int x){
        num = 0, len = -INF, ml = -INF;
        full(tmp, 0);
        int i = 0;
        for(; i < m; i ++){
            if(ask[i].dis <= x) break;
            int u = ask[i].u, v = ask[i].v, f = ask[i].lca;
            tmp[u] ++, tmp[v] ++, tmp[f] -= 2;
            num ++;
            ml = max(ml, ask[i].dis);
        }
        if(i == 0) return true;
        dfs3(1, 0);
        return ml - len <= x;
    }
    
    void solve(){
        sort(ask, ask + m);
        int l = 0, r = ask[0].dis;
        while(l < r){
            int mid = (l + r) >> 1;
            if(check(mid)) r = mid;
            else l = mid + 1;
        }
        ans = l;
    }
    
    int main(){
    
        full(head, -1);
        full(son, -1);
        n = read(), m = read();
        for(int i = 0; i < n - 1; i ++){
            int u = read(), v = read(), w = read();
            addEdge(u, v, w), addEdge(v, u, w);
        }
        dfs1(1, 0), dfs2(1, 1);
        buildTree(1, 1, n);
        for(int i = 0; i < m; i ++){
            ask[i].u = read(), ask[i].v = read();
            //cout << i << " " << ask[i].u << " " << ask[i].v << endl;
            lca(ask[i].u, ask[i].v, i);
        }
        solve();
        printf("%d
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    [置顶] windows player,wzplayerV2 for windows
    wzplayer 近期将会支持BlackBerry和WinPhone8
    wzplayerEx for android(真正硬解接口,支持加密的 player)
    ffmpeg for ios 交叉编译 (支持i686 armv7 armv7s) 包含lame支持
    ffmpeg for ios 交叉编译 (支持i686 armv7 armv7s) 包含lame支持
    编译cegcc 0.59.1
    wzplayer 近期将会支持BlackBerry和WinPhone8
    wzplayerEx for android(真正硬解接口,支持加密的 player)
    windows player,wzplayerV2 for windows(20140416)更新
    编译cegcc 0.59.1
  • 原文地址:https://www.cnblogs.com/onionQAQ/p/10660285.html
Copyright © 2011-2022 走看看