Bzoj3672: [Noi2014]购票

题面

传送门

Sol

(f[i])表示(i)到根的最小代价
(f[i])可以由(f[j])转移而来,要求(j)(i)的父亲,并且满足距离限制

显然(DP)式可以斜率优化
然而这是在树上,并且每次都要一个(i)往上的若干个点的凸包

可以考虑维护区间凸包,可以用线段树
或者(CDQ)分治

(CDQ)分治的方法,其实是点分治,每次先解决重心到其祖先那一块
然后考虑它们对其它子树的贡献,区间凸包可以转化为后缀凸包
排序后每次在前面加入并查询即可

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

IL ll Input(){
    RG ll x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

const int maxn(2e5 + 5);

int n, first[maxn], cnt;
int vis[maxn], size[maxn], mx[maxn], rt, sz, fa[maxn];
int q1[maxn], q2[maxn], l1, r1, len, anc[maxn], num;
ll deep[maxn], s[maxn], f[maxn], l[maxn], q[maxn], p[maxn];

struct Edge{
    int to, next;
    ll w;
} edge[maxn << 1];

IL void Add(RG int u, RG int v, RG ll w){
    edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
}

IL void Dfs(RG int u, RG int ff){
    s[u] = deep[u] * p[u] + q[u], l[u] = deep[u] - l[u];
    for(RG int e = first[u]; e != -1; e = edge[e].next){
        RG int v = edge[e].to;
        if(v != ff){
            deep[v] = deep[u] + edge[e].w;
            fa[v] = u, Dfs(v, u);
        }
    }
}

IL void GetRoot(RG int u, RG int ff){
    size[u] = 1, mx[u] = 0;
    for(RG int e = first[u]; e != -1; e = edge[e].next){
        RG int v = edge[e].to;
        if(v != ff && !vis[v]){
            GetRoot(v, u);
            size[u] += size[v];
            mx[u] = max(mx[u], size[v]);
        }
    }
    mx[u] = max(mx[u], sz - size[u]);
    if(mx[u] < mx[rt]) rt = u;
}

IL void GetSon(RG int u, RG int ff){
    q2[++len] = u;
    for(RG int e = first[u]; e != -1; e = edge[e].next){
        RG int v = edge[e].to;
        if(!vis[v] && v != ff) GetSon(v, u);
    }
}

IL int Cmp(RG int x, RG int y){
    return l[x] > l[y];
}

IL double Slope(RG int x, RG int y){
    return 1.0 * (f[x] - f[y]) / (1.0 * (deep[x] - deep[y]));
}

IL void Solve(RG int last, RG int u){
    vis[u] = 1;
    for(RG int e = first[u]; e != -1; e = edge[e].next)
        if(edge[e].to == fa[u] && !vis[edge[e].to]){
            rt = 0, sz = size[edge[e].to];
            GetRoot(edge[e].to, u), Solve(last, rt);
        }
    num = len = 0, GetSon(u, fa[u]);
    for(RG int i = u; i != last; i = fa[i]) anc[++num] = fa[i];
    l1 = 0, r1 = -1, sort(q2 + 1, q2 + len + 1, Cmp);
    for(RG int i = 1, j = 1; i <= len; ++i){
        while(j <= num && deep[anc[j]] >= l[q2[i]]){
            while(l1 < r1 && Slope(q1[r1 - 1], q1[r1]) <= Slope(q1[r1 - 1], anc[j])) --r1;
            q1[++r1] = anc[j++];
        }
        if(l1 <= r1){
            RG int ql = l1 + 1, qr = r1, pos = l1;
            while(ql <= qr){
                RG int mid = (ql + qr) >> 1;
                if(Slope(q1[mid - 1], q1[mid]) > p[q2[i]]) pos = mid, ql = mid + 1;
                else qr = mid - 1;
            }
            f[q2[i]] = min(f[q2[i]], f[q1[pos]] + s[q2[i]] - deep[q1[pos]] * p[q2[i]]);
        }
    }
    for(RG int e = first[u]; e != -1; e = edge[e].next)
        if(edge[e].to != fa[u] && !vis[edge[e].to]){
            rt = 0, sz = size[edge[e].to];
            GetRoot(edge[e].to, u), Solve(u, rt);
        }
}

int main(){
    n = Input(), Input();
    for(RG int i = 1; i <= n; ++i) first[i] = -1, f[i] = 1e18;
    mx[0] = n + 1, sz = n, f[1] = 0;
    for(RG int i = 2; i <= n; ++i){
        RG ll ff = Input(), w = Input();
        Add(ff, i, w), Add(i, ff, w);
        p[i] = Input(), q[i] = Input(), l[i] = Input();
    }
    Dfs(1, 0), GetRoot(1, 0), Solve(1, rt);
    for(RG int i = 2; i <= n; ++i) printf("%lld
", f[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/cjoieryl/p/9115889.html