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\n", dp[i]);
    }
//    fclose(stdin);
//    fclose(stdout);
    return 0;
}
View Code

 

posted @ 2018-03-05 15:41  19992147  阅读(165)  评论(0编辑  收藏  举报