BZOJ 3672 NOI 2014 购票

题面

Description

今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会。
全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号。其中SZ市的编号为 1。对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv 以及到父亲城市道路的长度 sv。
从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到达 a。再选择城市 a 的一个祖先 b,支付费用并到达 b。以此类推,直至到达SZ市。
对于任意一个城市 v,我们会给出一个交通工具的距离限制 lv。对于城市 v 的祖先 a,只有当它们之间所有道路的总长度不超过 lv 时,从城市 v 才可以通过一次购票到达城市 a,否则不能通过一次购票到达。对于每个城市 v,我们还会给出两个非负整数 pv,qv 作为票价参数。若城市 v 到城市 a 所有道路的总长度为 d,那么从城市 v 到城市 a 购买的票价为 dpv+qv。
每个城市的OIer都希望自己到达SZ市时,用于购票的总资金最少。你的任务就是,告诉每个城市的OIer他们所花的最少资金是多少。

Input

第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到)。输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市。其中第 v 行包含 5 个非负整数 f_v,s_v,p_v,q_v,l_v,分别表示城市 v 的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。请注意:输入不包含编号为 1 的SZ市,第 2 行到第 n 行分别描述的是城市 2 到城市 n。

Output

输出包含 n-1 行,每行包含一个整数。其中第 v 行表示从城市 v+1 出发,到达SZ市最少的购票费用。同样请注意:输出不包含编号为 1 的SZ市。

Samples

input:

7 3
1 2 20 0 3
1 5 10 100 5
2 4 10 10 10
2 9 1 100 10
3 5 20 100 10
4 4 20 0 10

output:

40
150
70
149
300
150

HINT

对于所有测试数据,保证\(n \le 2 \times 10^5\), \(0≤p_v≤10^6\)\(0≤q_v≤10^{12}\)\(1≤f_v<v\);保证 \(0<s_v≤l_v≤2×10^{11}\),且任意城市到SZ市的总路程长度不超过 \(2×10^{11}\)
至于数据类型什么的, 直接把它忽略掉, 整题AC即可.

Solution

做法貌似有很多, 但暂时来说会的就只有一种大暴力了:
首先我们不难写出一个斜率优化的式子:

\[\frac{f_j - f_k}{ dep_j - dep_k} < p_i \]

考虑到不具有单调性, 我们要用二分的方法求出最优解; 又由于题目限制了我们可以取的\(j\)\(k\)的范围, 考虑进行树链剖分病用线段树维护, 线段树上每个点维护其所包含区间的每个点组成的凸包, 在线段树上查找即可.
时间复杂度:
构建: \(O(n \log^2 n)\)
查询: \(O(n \log^3 n)\)


woc这个\(O(n \log^3 n)\)的方法还真TM能过啊, 当然仅限BZOJ... 这还是多亏了BZOJ的计时方法233

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define vector std::vector
#define max std::max
#define min std::min
#define sort std::sort

namespace Zeonfai
{
    inline long long getInt()
    {
        long long a = 0, sgn = 1; char c;
        while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
        while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
        return a * sgn;
    }
}
const int N = (int)2e5;
const long long INF = (long long)4e18;
int n;
struct point
{
    long long x, y; int id;
    inline point() {}
    inline point(long long _x, long long _y, int _id) {x = _x; y = _y; id = _id;}
    inline int operator <(const point a) const {return x < a.x;}
};
inline double slope(point a, point b)
{
    if(a.x == b.x) return a.y > b.y ? - 1e30 : 1e30; else return (double)(a.y - b.y) / (a.x - b.x);
}
struct segmentTree
{
    struct node
    {
        point *bck; int cnt;
        inline node() {bck = NULL; cnt = 0;}
    }nd[N << 2];
    void insert(int u, int L, int R, point p, int pos)
    {
        if(L == R) {nd[u].bck = new point[nd[u].cnt = 1]; nd[u].bck[0] = p; return;}
        if(pos <= L + R >> 1) insert(u << 1, L, L + R >> 1, p, pos); else insert(u << 1 | 1, (L + R >> 1) + 1, R, p, pos);
        if(nd[u << 1].cnt && nd[u << 1 | 1].cnt)
        {
            static point bck[N], stk[N]; int cnt = 0, tp = 0;
            for(int i = 0; i < nd[u << 1].cnt; ++ i) bck[cnt ++] = nd[u << 1].bck[i]; for(int i = 0; i < nd[u << 1 | 1].cnt; ++ i) bck[cnt ++] = nd[u << 1 | 1].bck[i];
            sort(bck, bck + cnt);
            for(int i = 0; i < cnt; ++ i)
            {
                while(tp >= 2 && slope(stk[tp - 1], stk[tp - 2]) > slope(bck[i], stk[tp - 1])) -- tp;
                stk[tp ++] = bck[i];
            }
            nd[u].bck = new point[nd[u].cnt = tp];
            for(int i = 0; i < tp; ++ i) nd[u].bck[i] = stk[i];
        }
    }
    inline void insert(point p, int pos) {insert(1, 1, n, p, pos);}
    vector<int> query(int u, int curL, int curR, int L, int R, long long slp)
    {
        vector<int> res; res.clear();
        if(curL >= L && curR <= R)
        {
            L = 0, R = nd[u].cnt - 1; int ans;
            while(L <= R)
            {
                int mid = L + R >> 1;
                if(! mid || slope(nd[u].bck[mid - 1], nd[u].bck[mid]) < slp) ans = nd[u].bck[mid].id, L = mid + 1;
                else R = mid - 1;
            }
            res.push_back(ans);
            return res;
        }
        int mid = curL + curR >> 1;
        if(L <= mid)
        {
            vector<int> tmp = query(u << 1, curL, mid, L, R, slp);
            // for(auto id : tmp) res.push_back(id);
            for(vector<int>::iterator id = tmp.begin(); id != tmp.end(); ++ id) res.push_back(*id);
            tmp.clear();
        }
        if(R > mid)
        {
            vector<int> tmp = query(u << 1 | 1, mid + 1, curR, L, R, slp);
            // for(auto id : tmp) res.push_back(id);
            for(vector<int>::iterator id = tmp.begin(); id != tmp.end(); ++ id) res.push_back(*id);
            tmp.clear();
        }
        return res;
    }
    inline vector<int> query(int L, int R, long long slp) {return query(1, 1, n, L, R, slp);}
}seg;
struct tree
{
    struct node
    {
        vector<int> suc;
        long long s, p, q, lim;
        int pre, sz, hvy, tp, dfn;
        long long dep, ans;
        inline node() {suc.clear();}
    }nd[N + 1];
    void getSize(int u, int pre)
    {
        nd[u].sz = 1; nd[u].hvy = -1; nd[u].pre = pre; nd[u].dep = ~ pre ? nd[pre].dep + nd[u].s : 0;
        for(auto v : nd[u].suc) {getSize(v, u); nd[u].sz += nd[v].sz; if(nd[u].hvy == -1 || nd[v].sz > nd[nd[u].hvy].sz) nd[u].hvy = v;}
        // for(vector<int>::iterator v = nd[u].suc.begin(); v != nd[u].suc.end(); ++ v) {getSize(*v, u); nd[u].sz += nd[*v].sz; if(nd[u].hvy == -1 || nd[*v].sz > nd[nd[u].hvy].sz) nd[u].hvy = *v;}
    }
    int clk, idx[N + 1];
    void work(int u, int tp)
    {
        nd[u].tp = tp; nd[u].dfn = ++ clk; idx[clk] = u;  nd[u].ans = INF;
        if(u != 1)
        {
            nd[u].ans = INF; int _u = nd[u].pre;
            while(nd[nd[nd[_u].tp].pre].dep > nd[u].dep - nd[u].lim && nd[_u].tp != 1)
            {
                vector<int> res = seg.query(nd[nd[_u].tp].dfn, nd[_u].dfn, nd[u].p);
                for(auto id : res) nd[u].ans = min(nd[u].ans, nd[id].ans + (nd[u].dep - nd[id].dep) * nd[u].p + nd[u].q);
                // for(vector<int>::iterator id = res.begin(); id != res.end(); ++ id) nd[u].ans = min(nd[u].ans, nd[*id].ans + (nd[u].dep - nd[*id].dep) * nd[u].p + nd[u].q);
                _u = nd[nd[_u].tp].pre; res.clear();
            }   
            int L = nd[nd[_u].tp].dfn, R = nd[_u].dfn, tmp;
            while(L <= R)
            {
                int mid = L + R >> 1;
                if(nd[u].dep - nd[idx[mid]].dep <= nd[u].lim) {tmp = mid; R = mid - 1;} else L = mid + 1;
            }
            vector<int> res = seg.query(tmp, nd[_u].dfn, nd[u].p);
            for(auto id : res) nd[u].ans = min(nd[u].ans, nd[id].ans + (nd[u].dep - nd[id].dep) * nd[u].p + nd[u].q);
            // for(vector<int>::iterator id = res.begin(); id != res.end(); ++ id) nd[u].ans = min(nd[u].ans, nd[*id].ans + (nd[u].dep - nd[*id].dep) * nd[u].p + nd[u].q);
            res.clear();
        }
        else nd[u].ans = 0;
        seg.insert(point(nd[u].dep, nd[u].ans, u), nd[u].dfn);
        if(~ nd[u].hvy) work(nd[u].hvy, tp);
        for(auto v : nd[u].suc) if(v != nd[u].hvy) work(v, v);
        // for(vector<int>::iterator v = nd[u].suc.begin(); v != nd[u].suc.end(); ++ v) if(*v != nd[u].hvy) work(*v, *v);
    }
    inline void decomposition() {getSize(1, -1); clk = 0; work(1, 1);}
}T;
int main()
{

    #ifndef ONLINE_JUDGE

    freopen("BZOJ3672.in", "r", stdin);
    freopen("BZOJ3672.out", "w", stdout);

    #endif

    using namespace Zeonfai;
    n = getInt(); getInt();
    for(int i = 2, pre; i <= n; ++ i) pre = getInt(), T.nd[pre].suc.push_back(i), T.nd[i].s = getInt(), T.nd[i].p = getInt(), T.nd[i].q = getInt(), T.nd[i].lim = getInt();
    T.decomposition();
    for(int i = 2; i <= n; ++ i) printf("%lld\n", T.nd[i].ans);
}

posted @ 2017-08-28 15:12  Zeonfai  阅读(256)  评论(0编辑  收藏  举报