zoukankan      html  css  js  c++  java
  • BZOJ 3672 购票

    Description

    今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国(n)个城市的OIer们都会从各地出发,到SZ市参加这次盛会。
    全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的(n)个城市用(1)(n)的整数编号。其中SZ市的编号为(1)。对于除SZ市之外的任意一个城市(v),我们给出了它在这棵树上的父亲城市(f_{v})以及到父亲城市道路的长度(s_{v})
    从城市(v)前往SZ市的方法为:选择城市(v)的一个祖先(a),支付购票的费用,乘坐交通工具到达(a)。再选择城市(a)的一个祖先(b),支付费用并到达(b)。以此类推,直至到达SZ市。
    对于任意一个城市(v),我们会给出一个交通工具的距离限制(l_{v})。对于城市(v)的祖先(a),只有当它们之间所有道路的总长度不超过(lv)时,从城市(v)才可以通过一次购票到达城市(a),否则不能通过一次购票到达。对于每个城市(v),我们还会给出两个非负整数(p_{v},q_{v})作为票价参数。若城市(v)到城市(a)所有道路的总长度为(d),那么从城市(v)到城市 (a)购买的票价为(d imes p_{v}+q_{v})
    每个城市的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市。

    Sample 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

    Sample Output

    40
    150
    70
    149
    300
    150

    Hint

    首先另(d_{i})表示(i)(1)的距离,我们可以先写出dp方程$$f_{i} = min lbrace f_{j}+(d_{i}-d_{j}P_{i}+Q_{j}) brace$$
    然后化简一下就可得到$$f_{i}=min lbrace f_{j}+P_{i}d_{i}+q_{i}-P_{i}d_{j} brace$$
    令$$x = d_{j},y = f_{j}+P_{i}d_{}+Q_{i},k = P_{i},b = f_{i}$$就是标准的斜率优化(b=y-kx)的式子了。维护一个下凸壳即可。
    于是考虑如何维护这个下凸壳,由于对(i)有用的(j)一定是(i)的祖先,所以明显从上往下更新,于是就有树链剖分。
    但是好像不好动态维护凸壳,但是我们发现从上往下加点(x=d_{i})是递增的,所以没必要二分,直接叉积就行了。
    至于满足(L_{i})的限制,直接在树上用树链剖分找出一个满足条件的最高点,再做询问就没问题了。时间复杂度(O(nlog^{3}n))达不到上界。

    #include<iostream>
    #include<cmath>
    #include<vector>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    
    typedef long long ll;
    #define eps (1e-6)
    #define maxn (400010)
    int N,side[maxn],toit[maxn],next[maxn],cnt,size[maxn],id[maxn],father[maxn];
    int top[maxn]; ll len[maxn],d[maxn],f[maxn],Map[maxn],P[maxn],Q[maxn],L[maxn];
    struct node
    {
    	ll x,y;
    	friend inline int operator /(const node &a,const node &b)
    	{
    		double res = 1.0*a.x*b.y-1.0*b.x*a.y;
    		if (fabs(res-0) <= eps) return 0;
    		else if (res > 0) return 1; else return -1;
    	}
    	friend inline node operator -(const node &a,const node &b) { return (node){a.x-b.x,a.y-b.y}; }
    	inline double angle() { return 1.0*y/x; }
    };
    vector <node> convex[maxn*2]; vector <double> slop[maxn*2];
    
    inline void add(int a,int b,ll c) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; len[cnt] = c; }
    inline void ins(int a,int b,ll c) { add(a,b,c); add(b,a,c); }
    
    inline ll read()
    {
    	char ch; ll ret = 0,f = 1;
    	do ch = getchar(); while (!(ch >= '0'&&ch <= '9')&&ch != '-');
    	if (ch == '-') ch = getchar(),f = -1;
    	do ret = ret*10+(ll)(ch-'0'),ch = getchar(); while (ch >= '0'&&ch <= '9');
    	return f*ret;
    }
    
    inline void dfs(int now)
    {
    	size[now] = 1;
    	for (int i = side[now];i;i = next[i])
    	{
    		if (toit[i] == father[now]) continue;
    		father[toit[i]] = now; d[toit[i]] = d[now]+len[i];
    		dfs(toit[i]); size[now] += size[toit[i]];
    	} 
    }
    
    inline void Div(int now,int Top)
    {
    	top[now] = Top; id[now] = ++cnt; Map[cnt] = now;
    	int heavy = 0;
    	for (int i = side[now];i;i = next[i])
    		if (toit[i] != father[now]&&size[toit[i]] > size[heavy]) heavy = toit[i];
    	if (!heavy) return; Div(heavy,Top);
    	for (int i = side[now];i;i = next[i]) if (toit[i] != father[now]&&toit[i] != heavy) Div(toit[i],toit[i]);
    }
    
    inline ll br(int now,ll key)
    {
    	int l = 0,r = slop[now].size()-1,mid;
    	while (l <= r)
    	{
    		mid = (l+r) >> 1;
    		if (slop[now][mid] < key) l = mid+1;
    		else r = mid-1;		
    	}
    	return convex[now][l].y-key*convex[now][l].x;
    }
    
    inline ll query(int now,int l,int r,int ql,int qr,ll key)
    {
    	if (ql <= l&&r <= qr) return br(now,key);
    	int mid = (l+r)>>1;
    	if (qr <= mid) return query(now<<1,l,mid,ql,qr,key);
    	else if (ql > mid) return query(now<<1|1,mid+1,r,ql,qr,key);
    	else return min(query(now<<1,l,mid,ql,mid,key),query(now<<1|1,mid+1,r,mid+1,qr,key));
    }
    inline void insert(int now,int l,int r,int pos,const node &pp)
    {
    	int nn = convex[now].size();
    	while (nn>1&&(pp-convex[now][nn-1])/(convex[now][nn-1]-convex[now][nn-2]) >= 0)
    		--nn,convex[now].pop_back(),slop[now].pop_back();
    	++nn; convex[now].push_back(pp);
    	if (nn > 1) slop[now].push_back((convex[now][nn-1]-convex[now][nn-2]).angle());
    	if (l == r) return; int mid = (l+r) >> 1;
    	if (pos <= mid) insert(now<<1,l,mid,pos,pp);
    	else insert(now<<1|1,mid+1,r,pos,pp);
    }
    
    inline void work(int now)
    {
    	int Top = now,i; f[now] = 1LL<<60;
    	while (true)
    	{
    		if (L[now] >= d[Top]-d[father[top[Top]]]) L[now] -= d[Top]-d[father[top[Top]]],Top = father[top[Top]];
    		else
    		{
    			int l = id[top[Top]],r = id[Top],mid;
    			while (l <= r)
    			{
    				mid = (l+r)>>1;
    				if (d[Top]-d[Map[mid]] > L[now]) l = mid+1;
    				else r = mid-1;
    			}
    			Top = Map[l]; break;
    		}
    	}
    	if (Top == now) return;
    	for (i = father[now];top[i] != top[Top];i = father[top[i]])
    		f[now] = min(query(1,1,N,id[top[i]],id[i],P[now]),f[now]);
    	f[now] = min(query(1,1,N,id[Top],id[i],P[now]),f[now]);
    	f[now] += P[now]*d[now]+Q[now];
    }
    
    inline void Dfs(int now)
    {
    	work(now);
    	insert(1,1,N,id[now],(node){d[now],f[now]});
    	for (int i = side[now];i;i = next[i])
    		if (toit[i] != father[now]) Dfs(toit[i]);
    }
    
    int main()
    {
    	freopen("3672.in","r",stdin);
    	freopen("3672.out","w",stdout);
    	N = read(); read();
    	for (int i = 2,a,b;i <= N;++i)
    	{
    		a = read(),b = read(),ins(i,a,b);
    		P[i] = read(),Q[i] = read(),L[i] = read();		
    	}
    	d[cnt = 0] = -1LL << 60; dfs(1); Div(1,1); insert(1,1,N,id[1],(node){0,0});
    	for (int i = side[1];i;i = next[i]) Dfs(toit[i]);
    	for (int i = 2;i <= N;++i) printf("%lld
    ",f[i]);
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    Debian如何永久添加静态路由
    一句话换个说法的软件
    高级程序员简历,技术总监喜欢什么简历?
    CP936实际上是GBK,编码问题再次让我熬夜
    句子说法转换的软件,基于AI技术
    输入关键词自动生成文章(2020年人工智能写作)
    自动写文章的智能软件(基于AI写作)
    python调用接口,python接收post请求接口(附完整代码)
    人工智能是铁饭碗还是铁坑,看看人工智能博士怎么说
    远离外包公司,就是远离码农的血汗工厂
  • 原文地址:https://www.cnblogs.com/mmlz/p/4606306.html
Copyright © 2011-2022 走看看