zoukankan      html  css  js  c++  java
  • [NOI2014]购票

    题目描述

    今年夏天,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他们所花的最少资金是多少。

    输入输出格式

    输入格式:

    第 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。

    输出格式:

    输出包含 n-1 行,每行包含一个整数。

    其中第 v 行表示从城市 v+1 出发,到达SZ市最少的购票费用。

    同样请注意:输出不包含编号为 1 的SZ市。

    输入输出样例

    输入样例#1:

    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

    输出样例#1:

    40
    150
    70
    149
    300
    150

    说明

    从每个城市出发到达 SZ的路线如下(其中箭头表示一次直达):

    城市 2:只能选择 2 → 1,花费为 2 × 20 + 0 = 40。

    城市 3:只能选择 3 → 1,花费为 5 × 10 + 100 = 150。 城 市 4 : 由 于 4 + 2 =6 ≤ l4 = 10,故可以选择 4 →1。若选择 4 → 1,花费为 (4 +2) × 10 + 10 = 70 ; 若选 择 4 → 2 → 1,则花费为 (4 ×10 + 10) + (2 × 20 + 0) =90;因此选择 4 → 1。

    城市 5:只能选择 5 →2 → 1 , 花费为 (9 × 1 +100) + (2 × 20 + 0) = 149;无法选择 5 → 1,因为 l5 =10,而城市 5 到城市 1 总路程为 9 + 2 = 11 > 5,城市 5 不能直达城市 1。

    城市 6:若选择 6 → 1,花费为 (5 + 5) × 20 + 100 = 300;若选择 6 → 3 →1,花费为 (5 × 20 + 100) + (5 × 10 + 100) = 350;因此选择 6 → 1。

    城市 7:选择 7 → 4 → 1,花费为 (4 × 20 + 0) + ((4 + 2) × 10 + 10) = 150;

    其他方案均比该方案差。


    题解


    凸包+线段树+链剖
    (n^2)的dp很显然
    按照dfs从小到大dp
    (f[u]=f[fa]+(dis[u]-dis[fa])*p[u]+q[u])(fa)是u的一个能到达的祖先
    然后可以发现这玩意儿可以移一下项
    (dis[fa] * p[u] - f[fa] * 1 = dis[u] * p[u] + q[u] - f[u])
    我们要让(f[u])最小,就是要让(dis[u] * p[u] + q[u] - f[u])最大
    这就变成了向凸包中插入((dis[fa],-f[fa])),查询((p[u],1))在凸包上的点积最大值
    就像向量集那道题那样用线段树维护一个区间内的下凸包
    然后就查询从这个点到根的那些区间的下凸包上的点和查询点点积的最大值
    凸包上三分即可
    复杂度(O(nlog^{3}{n}))

    代码

    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    # define LL long long
    # define int long long
    const int M = 200010 ; 
    const LL INF = 1e18 ;
    using namespace std ; 
    
    inline int read() {
        char c = getchar() ; int x = 0 , w = 1 ;
        while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
        while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
        return x*w ;
    }
    
    int n , num , hea[M] ;
    int fa[M] , dep[M] , sz[M] ;
    int top[M] , dfn[M] , idf[M] , cnt ;
    int np[M] , nq[M] , dislim[M] , bsn[M] , tp ;
    int st[M][20] , lg[M] ;
    LL dis[M] , f[M] ;
    struct Vec { LL x , y ; } po[M] , stp[M] ;
    struct E { int nxt , to , dis ; } edge[M * 2] ;
    inline bool Cross(Vec A , Vec B) {
    	return (double)A.x * B.y > (double)A.y * B.x ;
    }
    inline Vec operator - (Vec A , Vec B) { return (Vec) { A.x - B.x , A.y - B.y } ; }
    inline LL Dot(Vec A , Vec B) { return A.x * B.x + A.y * B.y ; }
    inline bool Cmpx(Vec A , Vec B) {
        if(A.x == B.x)
            return A.y < B.y ;
        return A.x < B.x ;
    }
    void add_edge(int from , int to , int dis) {
        edge[++num].nxt = hea[from] ; edge[num].to = to ;
        edge[num].dis = dis ; hea[from] = num ;
    }
    
    void dfs1(int u , int father) {
        fa[u] = father ; dep[u] = dep[father] + 1 ; sz[u] = 1 ; int mx = -1 ;
        for(int i = hea[u] ; i ; i = edge[i].nxt) {
            int v = edge[i].to ; if(v == father) continue ;
            dis[v] = dis[u] + edge[i].dis ; st[v][0] = u ;
            dfs1(v , u) ; sz[u] += sz[v] ;
            if(sz[v] > mx) mx = sz[v] , bsn[u] = v ;
        }
    }
    void dfs2(int u , int topf) {
        dfn[u] = ++ cnt ; idf[cnt] = u ; top[u] = topf ;
        if(!bsn[u]) return ; dfs2(bsn[u] , topf) ;
        for(int i = hea[u] ; i ; i = edge[i].nxt) {
            int v = edge[i].to ; if(!dfn[v]) 
                dfs2(v , v) ;
        }
    }
    struct Seg_Ment {
        # define ls	(now << 1)
        # define rs (now << 1 | 1)
        struct Node {
            int sz ;
            vector < Vec > vec ;
            Node () {  } ;
        } t[M * 4] ;  
        void build(int l , int r , int now) {
            t[now].sz = r - l + 1 ;
            if(l == r) return ; int mid = (l + r) >> 1 ;
            build(l , mid , ls) ; build(mid + 1 , r , rs) ;
        }
        inline void Make(int now) {
            tp = 0 ;
            int sz = t[now].sz ; if(sz == 1) return ;
            for(int i = 1 ; i <= sz ; i ++)
                po[i] = t[now].vec[i - 1] ;
            sort(po + 1 , po + sz + 1 , Cmpx) ;
            stp[++tp] = po[1] ; stp[++tp] = po[2] ;
            for(int i = 3 ; i <= sz ; i ++) {
                while(tp > 1 && !Cross(po[i] - stp[tp - 1] , stp[tp] - stp[tp - 1])) -- tp ;
                stp[++tp] = po[i] ;
            }
            t[now].vec.clear() ;
            for(int i = 1 ; i <= tp ; i ++)
                t[now].vec.push_back(stp[i]) ;
        }
        void Change(int x , Vec p , int l , int r , int now) {
            t[now].vec.push_back(p) ;
            if(t[now].vec.size() == t[now].sz) Make(now) ;
            if(l == r) return ; int mid = (l + r) >> 1 ;
            if(mid >= x) Change(x , p , l , mid , ls) ;
            else Change(x , p , mid + 1 , r , rs) ;
        }
        inline LL Gmax(vector < Vec > &ve , Vec p) {
            int l = 0 , r = ve.size() - 1 , lmid , rmid ;
            while(r - l >= 3) {
                lmid = (l + r) >> 1 ; rmid = (lmid + r) >> 1 ;
                if(Dot( ve[lmid] , p ) < Dot( ve[rmid] , p )) l = lmid ;
                else r = rmid ;
            }
            lmid = (l + r) >> 1 ; rmid = (lmid + r) >> 1 ;
            return max( max(Dot(ve[l] , p) , Dot(ve[r] , p)) , max(Dot(ve[lmid] , p) , Dot(ve[rmid] , p)) ) ;
        }
        LL query(int L , int R , Vec p , int l , int r , int now) {
            if(l == L && r == R) return Gmax(t[now].vec , p) ;
            int mid = (l + r) >> 1 ;
            if(mid >= R) return query(L , R , p , l , mid , ls) ;
            else if(mid < L) return query(L , R , p , mid + 1 , r , rs) ;
            else return max(query(L , mid , p , l , mid , ls) , query(mid + 1 , R , p , mid + 1 , r , rs)) ;
        }
        # undef ls
        # undef rs
    } T ;
    
    void ST() {
        for(int j = 1 ; j <= lg[n] + 1 ; j ++)
            for(int i = 1 ; i <= n ; i ++)
                st[i][j] = st[st[i][j - 1]][j - 1] ;
    }
    inline LL query(int u , int x , int y) {
        LL ret = - INF ;
        while(top[x] != top[y]) {
            if(dep[top[x]] < dep[top[y]]) swap(x , y) ;
            ret = max(ret , T.query(dfn[top[x]] , dfn[x] , (Vec) { np[u] , 1 } , 1 , n , 1)) ;
            x = fa[top[x]] ;
        }
        if(dep[x] > dep[y]) swap(x , y) ;
        ret = max(ret , T.query(dfn[x] , dfn[y] , (Vec) { np[u] , 1 } , 1 , n , 1)) ;
        return ret ;
    }
    
    # undef int
    int main() {
    # define int long long
        n = read() ; int Type = read() ;
        for(int i = 2 ; i <= n ; i ++) lg[i] = lg[i >> 1] + 1 ;
        for(int v = 2 , u , w ; v <= n ; v ++) {
            u = read() , w = read() ;
            add_edge(u , v , w) ;
            np[v] = read() ; nq[v] = read() ;
            dislim[v] = read() ;
        }
        dfs1(1 , 0) ; dfs2(1 , 1) ;
        ST() ; T.build(1 , n , 1) ;
        T.Change(1 , (Vec) { 0 , 0 } , 1 , n , 1) ;
        dis[0] = - INF ;
        for(int i = 2 , u , v ; i <= n ; i ++) {
            u = idf[i] ; v = idf[i] ;
            for(int j = lg[dep[v]] + 1 ; j >= 0 ; j --)
                if(dis[u] - dis[st[v][j]] <= dislim[u])
                    v = st[v][j] ;
            if(!v) v = 1 ;
            f[u] = dis[u] * np[u] + nq[u] - query(u , v , fa[u]) ;
            if(i == n) break ;
            T.Change(i , (Vec) { dis[u] , -f[u] } , 1 , n , 1) ;
        }
        for(int i = 2 ; i <= n ; i ++) 
    		printf("%lld
    ",f[i]) ;
        return 0 ;
    }
    
  • 相关阅读:
    react 采坑记录
    理解JS 模块化
    MongoDB使用教程
    scss
    gulp 使用教程
    node.js 简单入门
    jQuery
    jQuery
    php+mysql+bootstrap 实现成绩管理系统
    SVN的commit功能用bat实现
  • 原文地址:https://www.cnblogs.com/beretty/p/10454411.html
Copyright © 2011-2022 走看看