bzoj3672/uoj7[noi2014]购票

ljh2000 ljh2000     2022-08-22     262

关键词:

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

 

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

 

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

 

 

对于所有测试数据,保证 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

 

 

正解:CDQ分治+斜率优化DP+树分治

解题报告:

  这道题是三算法合一经典好题...

  我们考虑如果是在一个序列上,就是simple的斜率优化裸题了。但是P[i]的变化不随i变化而规律性变化,所以我每次要求的最优斜率是不一样的,维护好下凸包,每次在凸包上二分找到最优值即可。

  对于树上的距离限制,我们肯定对于每个点要重建凸包,如果每次重建显然复杂度无法承受,所以我们需要改变节点的处理顺序,使得祖先上的点顺次加入并且不用重构,这很简单,只要考虑每个点能被最上面的哪些点更新到,排个序就可以扫一遍做完了。

  另外树分治是为了保证我整体上的处理复杂度是log次,CDQ分治的最大精髓就是先递归处理一部分,再用处理完的部分来更新未处理的部分,显然当祖先节点递归处理完之后,就可以用来处理子树,而每棵子树内部的点都共用了一条链上的祖先,更新答案,最后再递归处理就可以了。

  说的有点抽象,看看代码理解起来还是挺快的。其实是我懒,不想写公式... 

 

 

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 200011;
const LL inf = (1LL<<62);
int n,f[MAXN],ecnt,first[MAXN],to[MAXN],next[MAXN],size[MAXN],maxS[MAXN],cnt,dui[MAXN];
LL p[MAXN],q[MAXN],lim[MAXN],dis[MAXN],w[MAXN],dp[MAXN];
bool use[MAXN];
struct node{ LL val; int id; }a[MAXN];
inline bool cmp(node q,node qq){ return q.val>qq.val; }
inline void link(int x,int y,LL z){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; }
inline LL upd(int i,int j){ return dp[j]+(dis[i]-dis[j])*p[i]+q[i]; }
inline LL K(int x,int y){ return (dp[y]-dp[x])/(dis[y]-dis[x]); }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline LL getLL(){
    LL w=0; LL q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline void dfs(int x){//预处理每个节点的dis和size
	size[x]=1;
	for(int i=first[x];i;i=next[i]) {
		dis[to[i]]=dis[x]+w[i];
		dfs(to[i]); size[x]+=size[to[i]];
	}
}

inline void find_root(int x,int S,int &rt){  
	maxS[x]=0; size[x]=1;
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(use[v]) continue;
		find_root(v,S,rt);
		size[x]+=size[v]; maxS[x]=max(maxS[x],size[v]);
	}
	maxS[x]=max(maxS[x],S-size[x]);
	if(maxS[x]<maxS[rt] && size[x]>1/*不然会GG!!!*/) rt=x;
}

inline void dfs2(int x){
	a[++cnt].id=x; a[cnt].val=dis[x]-lim[x];
	for(int i=first[x];i;i=next[i]) if(!use[to[i]]) dfs2(to[i]);
}

inline void solve(int x,int S){
	if(S==1) return ; int rt=0,now;
	find_root(x,S,rt);
	for(int i=first[rt];i;i=next[i]) use[to[i]]=1;//把重心的儿子节点堵上
	//CDQ分治
	//先处理除了重心的子树之外的部分
	solve(x,S-size[rt]+1);//重心也放入这个部分,方便以后讨论

	cnt=0; for(int i=first[rt];i;i=next[i]) dfs2(to[i]);
	sort(a+1,a+cnt+1,cmp);//把子树内所有点按能被更新到的最高高度自大往小排序
	now=rt;//需要向外更新的是,重心到当前分治根节点的这条链上的所有点
	int tail,l,r,mid,pos; tail=0;
	for(int i=1;i<=cnt;i++) {
		while(now!=f[x] && dis[a[i].id]-lim[a[i].id]<=dis[now]) {
			while(tail>1 && K(dui[tail],now)>=K(dui[tail-1],dui[tail])) tail--;
			//因为dis[now]递减,相当于是横坐标递减,可以看做是倒着加入,注意斜率判断方向
			dui[++tail]=now; now=f[now];
		}
		if(tail>0) {
			//在凸包上二分求得最优解
			l=1; r=tail; pos=1;
			while(l<=r) {
				mid=(l+r)>>1; if(mid==tail) { pos=tail; break; }
				if(K(dui[mid],dui[mid+1])>=p[a[i].id]) l=mid+1,pos=mid+1;//当前最优的应该是mid+1!!!
				else r=mid-1;
			}
			dp[a[i].id]=min(dp[a[i].id] , upd(a[i].id,dui[pos]));
		}
	}
	for(int i=first[rt];i;i=next[i]) solve(to[i],size[to[i]]);
}

inline void work(){
	n=getint(); LL x; x=getint();
	for(int i=2;i<=n;i++) {
		f[i]=getint(); x=getLL(); link(f[i],i,x);
		p[i]=getint(); q[i]=getLL(); lim[i]=getLL();
	}
	dfs(1); maxS[0]=n+1; for(int i=2;i<=n;i++) dp[i]=inf;
	solve(1,size[1]);
	for(int i=2;i<=n;i++) printf("%lld\n",dp[i]);
}

int main()
{
    work();
    return 0;
}

  

●bzoj3672[noi2014]购票

 题链:http://www.lydsy.com/JudgeOnline/problem.php?id=3672 题解:斜率优化DP,点分治(树上CDQ分治...) 这里有一个没有距离限制的简单版:BZOJ1767[Ceoi2009]harbingers定义$DP[i]$为从i出发到1号点的最小花费,$dis_i$为i到1号点的距离:转移:... 查看详情

bzoj3672[noi2014]购票树分治+斜率优化

【BZOJ3672】[Noi2014]购票Description 今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国n个城市的OIer们都会从各地出发,到SZ市参加这次盛会。    全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲... 查看详情

bzoj3672:[noi2014]购票

Description今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国n个城市的OIer们都会从各地出发,到SZ市参加这次盛会。全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的n... 查看详情

bzoj3672[noi2014]购票

推一下式子发现就是普通的斜率优化,但是放到了树上,那么我们怎么做呢,树上有什么能保证复杂度的求路径的算法呢,点分治!但是这是有根树,我们对于首先处理点分治后的重心以及与根相连的那个块,之后我们将块中剩... 查看详情

bzoj3672noi2014购票

这题好神啊..好神啊...好神啊...首先列出N2的DP方程较易.从DP方程很容易看出来是斜率优化.如何进一步优化?考虑对当前点以上的链建立一个下凸包.维护凸包就可以,但不是很好写.观察到方程可以必然由它的祖先节点转移.很像Cash... 查看详情

bzoj3672uoj#7noi2014购票

http://www.lydsy.com/JudgeOnline/problem.php?id=3672http://uoj.ac/problem/7链上的情况可以用斜率优化dp。树上用斜率优化dp时,单调队列的复杂度是均摊$O(n)$的,所以放到树上做“可持久化单调队列”复杂度是$O(n^2)$的,所以不能树上斜率... 查看详情

bzoj3672:[noi2014]购票(树形dp+斜率优化+可持久化凸包)

  这题的加强版,多了一个$l_i$的限制,少了一个$p_i$的单调性,难了好多...  首先有方程$f(i)=min{f(j)+(dep_i-dep_j)*p_i+q_i}$  $frac{f(j)-f(k)}{dep_j-dep_k}<p_i$  假如没有$l_i$的限制,实际上就是上面那题...  如果多了$l_i$的限... 查看详情

bzoj3672[noi2014]购票斜率优化+cdq分治+树的点分治

题目描述 给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$。问从每个点到1花费的最小代价(中途可以经停其它点)输入第1行包... 查看详情

bzoj3672购票点分治+dp

3672:[Noi2014]购票TimeLimit: 30Sec  MemoryLimit: 512MBSubmit: 1177  Solved: 562[Submit][Status][Discuss]Description 今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国n个城市的OIer们都会从 查看详情

bzoj-3672购票树分治+斜率优化dp

3672:[Noi2014]购票TimeLimit: 30Sec  MemoryLimit: 512MBSubmit: 1177  Solved: 562[Submit][Status][Discuss]Description 今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国n个城市的OIer们都会从 查看详情

noi2014购票

传送门另一个传送门这题劲啊……其实这题题解应该都烂大街了,不过我还是想大概写一下……就当是留作以后复习用也好……$t=0$的斜率优化很好搞。到了$t=1$的情况时,每个点都有转移区间的限制,做法大致就有线段树维护... 查看详情

[noi2014]购票---斜率优化+树形dp+数据结构(代码片段)

题目描述今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国n个城市的OIer们都会从各地出发,到SZ市参加这次盛会。全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的n... 查看详情

bzoj3669:[noi2014]魔法森林

地址:题目:3669:[Noi2014]魔法森林TimeLimit: 30Sec  MemoryLimit: 512MBSubmit: 2955  Solved: 1851[Submit][Status][Discuss]Description为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林 查看详情

[bzoj3670][noi2014]动物园

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3670[Noi2014]动物园TimeLimit: 10Sec  MemoryLimit: 512MBSubmit: 2644  Solved: 1425[Submit][Status][Discuss]Descr 查看详情

bzoj3669[noi2014]魔法森林

[Noi2014]魔法森林TimeLimit: 30Sec  MemoryLimit: 512MBSubmit: 3078  Solved: 1944[Submit][Status][Discuss]Description为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含... 查看详情

bzoj3670[noi2014]动物园

3670:[Noi2014]动物园TimeLimit: 10Sec  MemoryLimit: 512MBSubmit: 2032  Solved: 1077[Submit][Status][Discuss]Description近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动 查看详情

bzoj3670[noi2014]动物园

3670:[Noi2014]动物园TimeLimit: 10Sec  MemoryLimit: 512MBSubmit: 2080  Solved: 1107[Submit][Status][Discuss]Description近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动 查看详情

bzoj3669:[noi2014]魔法森林

#include<cstdio>#include<cstring>#include<algorithm>#defineLLunsignedintusingnamespacestd;constintM=100007,mod=51061;intread(){intans=0,f=1,c=getchar();while(c<‘0‘||c>‘9‘){if(c 查看详情