• bzoj3672


    点分治+cdq分治

    一看就是斜率优化,可惜在树上

    有一个比较显然的方法是树链剖分凸包

    但是复杂度较高

    然而这道题可以用cdq分治

    每次树分治的时候先处理朝向根的链的值

    然后再更新儿子

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <string>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int n, top1, top2, top, cnt = 1, rt;
    struct edge {
        int nxt, to;
        ll w;
    } e[N << 1];
    struct data {
        ll p, q, l;
    } a[N];
    int h[N], vis[N], fa[N], sz[N], mx[N], st[N], st1[N], st2[N];
    ll dep[N], dp[N];
    void link(int u, int v, ll w) {
        e[++cnt].nxt = h[u];
        h[u] = cnt;
        e[cnt].to = v;
        e[cnt].w = w;
    }
    bool cmp(int i, int j) {
        return dep[i] - a[i].l > dep[j] - a[j].l;
    }
    int getsize(int u, int last) {
        int ret = 1;
        for(int i = h[u]; i; i = e[i].nxt) {
            if(e[i].to == last || vis[e[i].to]) {
                continue;
            }
            ret += getsize(e[i].to, u);
        }
        return ret;
    } 
    void getroot(int u, int last, int S) {
        sz[u] = 1;
        mx[u] = 0;
        for(int i = h[u]; i; i = e[i].nxt) {
            if(e[i].to == last || vis[e[i].to]) {
                continue;
            }
            getroot(e[i].to, u, S);
            sz[u] += sz[e[i].to];
            mx[u] = max(mx[u], sz[e[i].to]);
        }
        mx[u] = max(mx[u], S - sz[u]);
        if(mx[u] < mx[rt]) {
            rt = u;
        }
    }
    void fill(int u, int last) {
        st2[++top2] = u;
        for(int i = h[u]; i; i = e[i].nxt) {
            if(e[i].to != last && !vis[e[i].to]) {
                fill(e[i].to, u);
            }
        }
    }
    double slope(int i, int j) {
        return (double)(dp[i] - dp[j]) / (dep[i] - dep[j]);
    }
    void update(int p) {
        if(!top) {
            return;
        }
        int l = 0, r = top, x = top;
        while(r - l > 1) {
            int mid = (l + r) >> 1;
            if(slope(st[mid], st[mid + 1]) < a[p].p) {
                r = x = mid;
            } else {
                l = mid;
            }
        }
        dp[p] = min(dp[p], dp[st[x]] + (dep[p] - dep[st[x]]) * a[p].p + a[p].q);
    } 
    void divide(int u) {
        rt = 0;
        getroot(u, 0, getsize(u, 0));
        vis[rt] = 1;
        int tmp = rt;
        if(u != rt) {
            divide(u);
        }
        rt = tmp;
        top1 = 0;
        top2 = 0;
        st1[++top1] = rt;
        for(int i = rt; i != u; i = fa[i]) {
            st1[++top1] = fa[i];
            if(dep[rt] - dep[fa[i]] <= a[rt].l) {
                dp[rt] = min(dp[rt], dp[fa[i]] + (dep[rt] - dep[fa[i]]) * a[rt].p + a[rt].q);
            }
        }
        for(int i = h[rt]; i; i = e[i].nxt) {
            if(!vis[e[i].to]) {
                fill(e[i].to, 0);
            }
        }
        sort(st2 + 1, st2 + top2 + 1, cmp);
        top = 0;
        int j = 1;
        for(int i = 1; i <= top1; ++i) {
            int v = st1[i];
            while(j <= top2 && dep[st2[j]] - a[st2[j]].l > dep[v]) {
                update(st2[j++]);
            }
            while(top > 1 && slope(st[top - 1], st[top]) < slope(st[top], v)) {
                --top;
            }
            st[++top] = v;
        }
        while(j <= top2) {
            update(st2[j++]);
        }
        for(int i = h[rt]; i; i = e[i].nxt) {
            if(!vis[e[i].to]) {
                divide(e[i].to);
            }
        }
    }
    int main() {
    //    freopen("ticket.in", "r", stdin);
    //    freopen("ticket.out", "w", stdout);
    //    freopen("output.txt", "w", stdout);
        int laji;
        scanf("%d%d", &n, &laji);
        memset(dp, 0x3f3f, sizeof(dp));
        dp[1] = 0;
        mx[0] = 0x3f3f3f3f;
        for(int i = 2; i <= n; ++i) {
            int f;
            ll d;
            scanf("%d%lld", &f, &d);
            fa[i] = f;
            scanf("%lld%lld%lld", &a[i].p, &a[i].q, &a[i].l);
            link(i, f, d);
            link(f, i, d);
            dep[i] = dep[f] + d;
        }
        divide(1);
        for(int i = 2; i <= n; ++i) {
            printf("%lld
    ", dp[i]);
        }
    //    fclose(stdin);
    //    fclose(stdout);
        return 0;
    }
    View Code
  • 相关阅读:
    软件测试:Homework 3
    软件测试:Lab 1
    软件测试:Homework 2
    软件测试:Homework 1
    JAVA的回忆
    Java 操作符
    Java 笔录
    经典C#编程理解,概要,经典
    网络精灵
    签到计时
  • 原文地址:https://www.cnblogs.com/19992147orz/p/8509317.html
Copyright © 2020-2023  润新知