[BZOJ3672][NOI2014]購票(斜率優化DP+樹鏈剖分+線段樹+三分)

題目:

我是超鏈接

題解:

這個很明顯要從上往下DP,而且下面的DP選擇了就可以直接選擇用上面的信息直接轉移了。

大概就是f[i]=min(p[i](dis[i]dis[j])+q[i]+f[j]) (dis[i]-dis[j]<=l[i],j=fa[j])

dis[i]當然是距離數組,f[i]就是答案數組啦
按照無關的量放在外面畫柿子吧,f[i]=min(p[i]dis[j]+f[j])+p[i]dis[i]+q[i]
那麼對於一個x=p[i]的情況,k=-dis[j],b=f[j],轉化爲y座標最小。這樣對於fa[i]到pre[i](最高滿足條件的祖先),我們有這些直線,這些直線肯定不會都有用啊,其實是要維護一個下凸殼,然後選擇出在橫座標爲p[i]的情況下最小值所在的直線就ok了。

假設我們已經有一個半平面和橫座標x,那麼我們可以在O(logn)的時間內找到使得y最小的那條線(三分)

既然是樹,那麼我們可以考慮一下樹鏈剖分,每條樹鏈用一個線段樹維護一下樹鏈上各個區間半平面交的結果,由於從祖先到子孫的dis[i]會遞增,所以我們有一個天然的斜率順序

所以這個可以用vector維護。

代碼:

因爲要會考就先咕了代碼叭QAQ
先弄個正確的代碼上來叭

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define ls x << 1
#define rs x << 1 | 1
#define eb emplace_back
#define pb pop_back
#define db double
const int MAX = 210033;
const ll inf = (1ll << 63) - 1;
struct Point
{
    ll x, y;
    Point() {}
    Point(ll a, ll b) { x = a, y = b; }
    friend Point operator - (Point a, Point b) { return Point(a.x - b.x, a.y - b.y); }
    friend ll operator * (Point a, Point b) { return a.x * b.y - a.y * b.x; }
};
struct edge
{
    int to; ll w;
    edge() {}
    edge(int a, ll b) { to = a, w = b; }
};
vector<edge> e[MAX];
vector<Point> h[MAX << 2];
int n, cnt_dfn;
int dfn[MAX], top[MAX], inv[MAX], siz[MAX], fa[MAX], que[MAX];
ll ans[MAX], P[MAX], Q[MAX], lim[MAX], dis[MAX];
void dfs1(int u)
{
    siz[u] = 1;
    for (auto v : e[u])
    {
        dis[v.to] = dis[u] + v.w;
        dfs1(v.to);
        siz[u] += siz[v.to];
    }
}
void dfs2(int u, int tp)
{
    int big = 0;
    top[u] = tp, dfn[u] = ++cnt_dfn, inv[cnt_dfn] = u;
    for (auto v : e[u])
        if (siz[v.to] > siz[big])
            big = v.to;
    if (!big) return;
    dfs2(big, tp);
    for (auto v : e[u])
        if (v.to != big)
            dfs2(v.to, v.to);
}
void bfs()
{
    int head, tail;
    que[head = tail = 1] = 1;
    while (head <= tail)
    {
        int u = que[head++];
        for (auto v : e[u])
            que[++tail] = v.to;
    }
}
void update(int x, int l, int r, int p, Point c)
{
    int siz = 0;
    while ((siz = h[x].size()) > 1 && (c - h[x][siz - 2]) * (h[x][siz - 1] - h[x][siz - 2]) > 0)
        h[x].pb();
    h[x].eb(c);
    if (l >= r) return;
    int mid = (l + r) >> 1;
    if (p <= mid) update(ls, l, mid, p, c);
    else update(rs, mid + 1, r, p, c);
}
ll query(int x, int l, int r, int L, int R, int id)
{
    ll Ans = inf;
    if (L <= l && r <= R)
    {
        int lx = 0, rx = h[x].size() - 1;
        while (rx - lx > 3)
        {
            int m1 = lx + (rx - lx) / 3, m2 = rx - (rx - lx) / 3;
            ll v1 = P[id] * (dis[id] - h[x][m1].x) + h[x][m1].y;
            ll v2 = P[id] * (dis[id] - h[x][m2].x) + h[x][m2].y;
            if (v1 <= v2) rx = m2;
            else lx = m1;
        }
        for (int i = lx; i <= rx; i++)
            Ans = min(Ans, P[id] * (dis[id] - h[x][i].x) + h[x][i].y + Q[id]);
        return Ans;
    }
    int mid = (l + r) >> 1;
    if (L <= mid) Ans = min(Ans, query(ls, l, mid, L, R, id));
    if (R > mid) Ans = min(Ans, query(rs, mid + 1, r, L, R, id));
    return Ans;
}
void calc(int L, int R, int id)
{
    ll Ans = inf;
    while (dis[top[R]] > dis[L])
    {
        Ans = min(Ans, query(1, 1, n, dfn[top[R]], dfn[R], id));
        R = fa[top[R]];
    }
    Ans = min(Ans, query(1, 1, n, dfn[L], dfn[R], id));
    ans[id] = Ans;
    update(1, 1, n, dfn[id], Point(dis[id], ans[id]));
}
main()
{
    scanf("%lld%lld", &n, &fa[MAX - 1]);
    for (int i = 2; i <= n; i++)
    {
        ll w;
        scanf("%lld%lld%lld%lld%lld", &fa[i], &w, &P[i], &Q[i], &lim[i]);
        e[fa[i]].eb(edge(i, w));
    }
    dfs1(1), dfs2(1, 1), bfs();
    update(1, 1, n, 1, Point(0, 0));
    for (int i = 2; i <= n; i++)
    {
        int anc = que[i], dist = dis[anc] - lim[anc];
        anc = fa[anc];
        while (anc > 1 && dis[fa[top[anc]]] >= dist)
            anc = fa[top[anc]];
        if (anc > 1)
        {
            int l = dfn[top[anc]], r = dfn[anc];
            while (l <= r)
            {
                int mid = (l + r) >> 1;
                if (dis[inv[mid]] >= dist)
                    anc = inv[mid], r = mid - 1;
                else l = mid + 1;
            }
        }
        else anc = 1;
        calc(anc, fa[que[i]], que[i]);
    }
    for (int i = 2; i <= n; i++)
        printf("%lld\n", ans[i]);
    return 0;
}
發佈了722 篇原創文章 · 獲贊 547 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章