[BZOJ3672][Noi2014]购票(斜率优化+点分治)

Address

BZOJ 3672
UOJ#7

Solution

dis[u] d i s [ u ] 表示点 u u 到根的距离。
f[u] 表示 u u 到根的最小费用。
top[u] 表示满足 v v u 的祖先, dis[u]dis[v]lu d i s [ u ] − d i s [ v ] ≤ l u v v 的深度最小的点 v
那么易得转移方程:

f[u]=minvudis[u]dis[v]lu{f[v]+(dis[u]dis[v])×pu+qu} f [ u ] = min v 是 u 的 祖 先 且 d i s [ u ] − d i s [ v ] ≤ l u { f [ v ] + ( d i s [ u ] − d i s [ v ] ) × p u + q u }

拆开括号并移项:
f[u]=minvudis[u]dis[v]lu{f[v]dis[v]×pu}+dis[u]×pu+qu f [ u ] = min v 是 u 的 祖 先 且 d i s [ u ] − d i s [ v ] ≤ l u { f [ v ] − d i s [ v ] × p u } + d i s [ u ] × p u + q u

相当于找一条斜率为 pu p u 的直线过点 (dis[v],f[v]) ( d i s [ v ] , f [ v ] ) 使得这条直线的截距 f[u] f [ u ] 最小。
于是把每个决策看作点 (dis[u],f[u]) ( d i s [ u ] , f [ u ] ) ,在任意时刻( f[u] f [ u ] 待计算时)维护根到 u u 的父亲的路径上所有决策点构成的下凸壳。由于 pu 没有单调性,故需要在凸壳上二分找最优决策点。 裆燃,这样的做法不对。
我们能够发现,此题主要的困难不是树结构,而在于转移条件 dis[u]dis[v]lu d i s [ u ] − d i s [ v ] ≤ l u
这意味着,如果 u u 的一个祖先 v 1 1 father(u) 的路径上不是凸壳上的点(不可能成为最优决策),它可能在 top[u] t o p [ u ] father(u) f a t h e r ( u ) 的路径上成为凸壳上的点(可能成为最优决策)。
先假设 这不是一棵树,是一个序列 i i 能从 [top[i],i1] 转移。 f[1]=0 f [ 1 ] = 0
可以 CDQ 分治。设现在要处理 f[l...r] f [ l . . . r ] mid=l+r2 m i d = ⌊ l + r 2 ⌋
(1)先处理 f[l...mid] f [ l . . . m i d ] 。(递归下去)。
(2)处理 f[l...mid] f [ l . . . m i d ] f[mid+1...r] f [ m i d + 1... r ] 的影响。具体地,将 f[mid+1...r] f [ m i d + 1... r ] 按照 top t o p 从大到小排序( top t o p 大于 mid m i d 的忽略),然后从 mid m i d l l 顺次将每个点加入凸壳,期间如果第 i 个点到第 mid m i d 个点已经加入凸壳并且存在一个 j[mid+1,r] j ∈ [ m i d + 1 , r ] 满足 top[j]=i t o p [ j ] = i 则在第 i i 个点到第 mid 个点的凸壳上二分来更新 f[j] f [ j ] 的值。
(3)处理 f[mid+1...r] f [ m i d + 1... r ] 。(递归下去)。
把这种算法迁移到树上,就是——点分治!!!!!
(1)找到树的重心 r r
(2)递归处理 r 的子树(不含 r r )之外的所有点构成的连通子树。
(3)将 r 的子树内(不含 r r )的点中, top 的深度小于等于 r 的深度的所有点 按照 top 的深度从大到小排序。
(4)从 v v 开始往当前处理的连通子树的根处理,每走到一个点就把一个点加入凸壳。
(5)如果一个时候,维护了 v x x 的凸壳且有 f[u]=x ,则凸壳上二分更新 f[u] f [ u ]
(6)枚举 v v 的每个子节点 w ,往 w w 的子树递归。
复杂度 O(nlog2n)

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Tree(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
typedef long long ll;

inline int read()
{
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}

inline ll readll()
{
    ll res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}

const int N = 2e5 + 5, LogN = 20;
const ll INF = ((1ll << 62) - 1 << 1) + 1;
int n, fa[N][LogN], p[N], ecnt, nxt[N], adj[N], go[N], sze[N], maxs[N],
dep[N], top[N], tot, z[N], len, bel[N];
ll f[N], q[N], s[N], l[N], dis[N];
bool vis[N];

struct point
{
    ll x, y;
} que[N];

inline bool comp(const int &a, const int &b)
{
    return dep[top[a]] > dep[top[b]];
}

template <class T>
T Max(T a, T b) {return a > b ? a : b;}

template <class T>
T Min(T a, T b) {return a < b ? a : b;}

void add_edge(int u, int v)
{
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}

void dfs(int u)
{
    dep[u] = dep[fa[u][0]] + 1;
    int i;
    For (i, 0, 16) fa[u][i + 1] = fa[fa[u][i]][i];
    Tree(u) dis[v] = dis[u] + s[v], dfs(v);
}

int extr(int u, ll lim)
{
    int i, v = u;
    Rof (i, 17, 0)
        if (fa[u][i] && dis[v] - dis[fa[u][i]] <= lim)
            u = fa[u][i];
    return u;
}

void dfs1(int u)
{
    sze[u] = 1; maxs[u] = 0;
    Tree(u) if (!vis[v])
        dfs1(v), sze[u] += sze[v],
        maxs[u] = Max(maxs[u], sze[v]);
}

void dfs2(int r, int u, int &G)
{
    if (Max(maxs[u], sze[r] - sze[u]) < Max(maxs[G], sze[r] - sze[G]))
        G = u;
    Tree(u) if (!vis[v]) dfs2(r, v, G);
}

int calcG(int u)
{
    int G = u;
    dfs1(u); dfs2(u, u, G);
    return G;
}

void dfs3(int rt, int u, int maxd)
{
    if (dep[top[u]] <= maxd)
        z[++tot] = u, bel[tot] = rt;
    Tree(u) if (!vis[v]) dfs3(rt, v, maxd);
}

double slope(point a, point b)
{
    return 1.0 * (a.y - b.y) / (a.x - b.x);
}

void convex_hull(point x)
{
    while (len > 1 && slope(x, que[len]) >= slope(que[len], que[len - 1]))
        len--;
    que[++len] = x;
}

ll query(ll sl)
{
    int l = 1, r = len - 1;
    while (l <= r)
    {
        int mid = l + r >> 1;
        ll tmp = (que[mid + 1].y - que[mid + 1].x * sl)
            - (que[mid].y - que[mid].x * sl);
        if (tmp >= 0) r = mid - 1;
        else l = mid + 1;
    }
    return que[l].y - que[l].x * sl;
}

void zzqioirank1(int u)
{
    int G = calcG(u); vis[G] = 1;
    if (u != G)
    {
        zzqioirank1(u);
        int v = fa[G][0];
        while (dep[v] >= dep[u] && dep[v] >= dep[top[G]])
            f[G] = Min(f[G], f[v] + (dis[G] - dis[v]) * p[G] + q[G]),
            v = fa[v][0];
    }
    tot = len = 0;
    Tree(G) if (!vis[v]) dfs3(v, v, dep[G]);
    sort(z + 1, z + tot + 1, comp);
    int i, v = G;
    For (i, 1, tot)
    {
        int u = z[i];
        while (dep[v] >= dep[top[u]])
            convex_hull((point) {dis[v], f[v]}), v = fa[v][0];
        f[u] = Min(f[u], query(p[u]) + dis[u] * p[u] + q[u]);
        top[u] = bel[i];
    }
    Tree(G) if (!vis[v]) zzqioirank1(v);
}

int main()
{
    int i;
    n = read(); read();
    For (i, 2, n)
    {
        f[i] = INF;
        fa[i][0] = read(); s[i] = readll();
        p[i] = read(); q[i] = readll();
        l[i] = readll();
        add_edge(fa[i][0], i);
    }
    dfs(1);
    For (i, 1, n) top[i] = extr(i, l[i]);
    zzqioirank1(1);
    For (i, 2, n) printf("%lld\n", f[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值