zoukankan      html  css  js  c++  java
  • [BZOJ3672][UOJ#7][NOI2014]购票

    [BZOJ3672][UOJ#7][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

    输入示例2

    传送门(点击下载)

    输出示例2

    传送门

    数据规模及约定

    对于所有测试数据,保证 0≤pv≤106,0≤qv≤1012,1≤fv<v;保证 0<sv≤lv≤2×1011,且任意城市到SZ市的总路程长度不超过 2×1011

    输入的 t 表示数据类型,0≤t<4,其中:

    当 t=0 或 2 时,对输入的所有城市 v,都有 fv=v-1,即所有城市构成一个以SZ市为终点的链;

    当 t=0 或 1 时,对输入的所有城市 v,都有 lv=2×1011,即没有移动的距离限制,每个城市都能到达它的所有祖先;

    当 t=3 时,数据没有特殊性质。

    n=2×10^5

    题解

    借着此题学了学有根树分治

    首先不难想出一个dp,设f(i)表示节点 i 到节点 1 所需的最小花费,有 f(i) = min{ f(j) + d(i ~ j) * p(i) + q(i) | j 为 i 祖先 & d(i ~ j) <= l(i) },其中d(i ~ j)表示节点 i 到 j 的距离,p, q, l 的意义见题目描述。

    还是基本的思想,把d(i ~ j)拆开,变成dep(i) - dep(j)(dep(i)表示节点 i 到根的距离),于是上述式子转化成 f(i) - dep[i] * p(i) - q(i) = min{ f(j) + p(i) * dep(j) | 条件略 },可以用分治把它转化成一个序列问题,有一定顺序后就可以维护下凸壳了。

    分治的思想是:对于子树u,找到其重心rt,分治处理u所在的以rt为根的子树,然后用u到rt这一条链上的信息更新以rt为根的其他子树上节点的信息。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <stack>
    #include <vector>
    #include <queue>
    #include <cstring>
    #include <string>
    #include <map>
    #include <set>
    using namespace std;
    #define LL long long
    #define LD double
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *tail;
    inline char Getchar() {
        if(Head == tail) {
            int l = fread(buffer, 1, BufferSize, stdin);
            tail = (Head = buffer) + l;
        }
        return *Head++;
    }
    LL read() {
        LL x = 0, f = 1; char c = Getchar();
        while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
        while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
        return x * f;
    }
    
    #define maxn 200010
    #define maxm 400010
    #define oo 1ll << 60
    int n, m, head[maxn], next[maxm], to[maxm], fa[maxn];
    LL dep[maxn], f[maxn], p[maxn], q[maxn], l[maxn];
    
    void AddEdge(int a, int b) {
    	to[++m] = b; next[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; next[m] = head[a]; head[a] = m;
    	return ;
    }
    
    int root, size, siz[maxn], g[maxn];
    bool vis[maxn];
    void getroot(int u, int pa) {
    	siz[u] = 1; int maxs = 0;
    	for(int e = head[u]; e; e = next[e]) if(!vis[to[e]] && to[e] != pa) {
    		getroot(to[e], u);
    		siz[u] += siz[to[e]];
    		maxs = max(maxs, siz[to[e]]);
    	}
    	g[u] = max(maxs, size - siz[u]);
    	if(g[u] < g[root]) root = u;
    	return ;
    }
    
    int A[maxn], cnt;
    void dfs(int u, int pa) {
    	A[++cnt] = u;
    	for(int e = head[u]; e; e = next[e]) if(!vis[to[e]] && to[e] != pa)
    		dfs(to[e], u);
    	return ;
    }
    bool cmp(int i, int j) { return dep[i] - l[i] > dep[j] - l[j]; }
    LD slop(int i, int j) { return (LD)(f[i] - f[j]) / (dep[i] - dep[j]); }
    int Q[maxn];
    void solve(int u) {
    	g[root = 0] = n+1; getroot(u, 0);
    	int ni = root, rt = root; vis[root] = 1;
    	if(!vis[u]) size = siz[u] - siz[rt], solve(u);
    //	printf("%d %d
    ", u, ni);
    	cnt = 0;
    	for(int e = head[ni]; e; e = next[e]) if(!vis[to[e]]) dfs(to[e], ni);
    	sort(A + 1, A + cnt + 1, cmp);
    	for(int i = fa[ni]; i != fa[u] && dep[i] >= dep[ni] - l[ni]; i = fa[i])
    		f[ni] = min(f[ni], f[i] + (dep[ni] - dep[i]) * p[ni] + q[ni]);
    	for(int i = 1, r = 0; i <= cnt; i++) {
    		for(; ni != fa[u] && dep[ni] >= dep[A[i]] - l[A[i]]; ni = fa[ni]) {
    			while(r > 1 && slop(Q[r-1], Q[r]) <= slop(Q[r], ni)) r--;
    			Q[++r] = ni;
    		}
    		int L = 1, R = r + 1;
    		while(R - L > 1) {
    			int M = L + R >> 1;
    			if(M == 1 || slop(Q[M-1], Q[M]) >= (LD)p[A[i]]) L = M;
    			else R = M;
    		}
    		if(L < R) f[A[i]] = min(f[A[i]], f[Q[L]] + (dep[A[i]] - dep[Q[L]]) * p[A[i]] + q[A[i]]);
    	}
    	for(int e = head[rt]; e; e = next[e]) if(!vis[to[e]]) {
    		size = siz[to[e]]; solve(to[e]);
    	}
    	return ;
    }
    
    int main() {
    //	freopen("ex_ticket2.in", "r", stdin);
    //	freopen("out.out", "w", stdout);
    	n = read(); read();
    	f[1] = 0;
    	for(int i = 2; i <= n; i++) {
    		f[i] = oo;
    		fa[i] = read(); AddEdge(fa[i], i);
    		dep[i] = dep[fa[i]] + read();
    		p[i] = read(); q[i] = read(); l[i] = read();
    	}
    	
    	size = n;
    	solve(1);
    	
    	for(int i = 2; i <= n; i++) printf("%lld
    ", f[i]);
    	
    	return 0;
    }
    
  • 相关阅读:
    Time Zone 【模拟时区转换】(HDU暑假2018多校第一场)
    HDU 1281 棋盘游戏 【二分图最大匹配】
    Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS换根 || 树形dp】
    Codeforces Round #527 (Div. 3) D2. Great Vova Wall (Version 2) 【思维】
    Codeforces Round #527 (Div. 3) D1. Great Vova Wall (Version 1) 【思维】
    Codeforces Round #528 (Div. 2, based on Technocup 2019 Elimination Round 4) C. Connect Three 【模拟】
    Avito Cool Challenge 2018 E. Missing Numbers 【枚举】
    Avito Cool Challenge 2018 C. Colorful Bricks 【排列组合】
    005 如何分析问题框架
    004 如何定义和澄清问题
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5420880.html
Copyright © 2011-2022 走看看