bzoj-3672 购票

题意:

给出一颗n个结点的有根树,边有长度;

每个点有可以购票前往长度相差不超过li的它的祖先,票的花费为pi*长度+qi;

当然的,可以的选择多次倒车到达;

求每个点到根的最小花费;

n<=200000;

题解:

这题真的好贴心,数据特殊情况都给你让你特判了2333;

首先一条链的情况都会吧,设f[i]为i到根的最小花费,dis为到根的距离;

转移方程为:f[i]=f[j]+p[i]*(dis[i]-dis[j])+q[i];

斜率优化搞搞就好了;

然而到了树上,和在序列上的思想基本一样;

但是多了很多细节处理;

分治自然是要分的,分的就应该是树的重心;

分治之后呢?

分治之后的树应该是是有根的,因为所有点的DP值都是由树上的祖先更新而来;

所以就用根到分治中心的这条链,去更新所有分治中心的其他子树;

但是和一般的序列上的不同,我们不能处理完凸包再二分去找答案;

那样会有可选的值遗漏;

所以将要更新的点放在一个序列中,按能够到的祖先排序;

排序之后就在这个序列里扫就好了,时间复杂度是O(L*logL);

然后带上树分治的复杂度,总复杂度O(nlog^2n);

细节颇多,树分治的姿势都是不熟嘛;

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 210000
using namespace std;
typedef long long ll;
int next[N],to[N],head[N],tot;
ll val[N];
int fa[N],size[N],st[N],top,qu[N],s,t,G,mi;
ll p[N],q[N],l[N],dis[N],f[N];
bool ban[N];
void add(int x,int y,ll v)
{
	to[++tot]=y;
	val[tot]=v;
	next[tot]=head[x];
	head[x]=tot;
}
void init(int x)
{
	size[x]=1;
	for(int i=head[x];i;i=next[i])
	{
		dis[to[i]]=dis[x]+val[i];
		init(to[i]);
		size[x]+=size[to[i]];
	}
}
bool cmp(int a,int b)
{
	return dis[a]-l[a]>dis[b]-l[b];
}
void getP(int x)
{
	st[++top]=x;
	for(int i=head[x];i;i=next[i])
		if(!ban[to[i]])
			getP(to[i]);
}
ll find(int x)
{
	if(s>t)	return 0x3f3f3f3f3f3f3f3fll;
	int l=s,r=t,mid;
	while(l<=r)
	{
		mid=l+r>>1;
		if(mid+1>t||(long double)(f[qu[mid+1]]-f[qu[mid]])/(dis[qu[mid+1]]-dis[qu[mid]])<p[x])
			r=mid-1;
		else
			l=mid+1;
	}
	return f[qu[l]]+p[x]*(dis[x]-dis[qu[l]])+q[x];
}
void getG(int x,int bk)
{
	size[x]=1;
	int i,mas=0,temp;
	for(i=head[x];i;i=next[i])
	{
		if(ban[to[i]])	continue;
		getG(to[i],bk);
		size[x]+=size[to[i]];
		mas=max(mas,size[to[i]]);
	}
	mas=max(mas,bk-size[x]);
	if(mas<=mi)
		mi=mas,G=x;
}
void slove(int x,int y)
{
	if(size[x]==1)	return ;
	int i,j,k;
	for(i=head[y];i;i=next[i])
		if(!ban[to[i]])
			ban[to[i]]=1;
	mi=0x3f3f3f3f;
	getG(x,size[x]);
	slove(x,G);
	top=0;
	for(i=head[y];i;i=next[i])
		getP(to[i]);
	sort(st+1,st+top+1,cmp);
	s=1,t=0;
	for(i=1,k=y;i<=top;i++)
	{
		while(k!=fa[x]&&dis[st[i]]-l[st[i]]<=dis[k])
		{
			while(s<t&&(long double)(f[qu[t-1]]-f[qu[t]])/(dis[qu[t-1]]-dis[qu[t]])<(long double)(f[qu[t]]-f[k])/(dis[qu[t]]-dis[k]))
				t--;
			qu[++t]=k;
			k=fa[k];
		}
		f[st[i]]=min(f[st[i]],find(st[i]));
	}
	for(i=head[y],k=size[x];i;i=next[i])
	{
		mi=0x3f3f3f3f;
		getG(to[i],size[to[i]]);
		slove(to[i],G);
	}
}
int main()
{
	int n,m,i,j,k,x,y;
	ll v;
	scanf("%d%d",&n,&m);
	for(i=2;i<=n;i++)
	{
		scanf("%d%lld%lld%lld%lld",fa+i,&v,p+i,q+i,l+i);
		add(fa[i],i,v);
	}
	ban[1]=0;
	init(1);
	memset(f,0x3f,sizeof(f));
	f[1]=0;
	mi=0x3f3f3f3f;
	getG(1,size[1]);
	slove(1,G);
	for(i=2;i<=n;i++)
		printf("%lld\n",f[i]);
	return 0;
}
时间: 2024-11-09 04:01:08

bzoj-3672 购票的相关文章

bzoj 3672 购票 点分治+dp

3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1177  Solved: 562[Submit][Status][Discuss] Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.

bzoj 3672: [Noi2014]购票 树链剖分+维护凸包

3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 480  Solved: 212[Submit][Status][Discuss] Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其

●BZOJ 3672 [Noi2014]购票

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3672 题解: 斜率优化DP,点分治(树上CDQ分治...) 这里有一个没有距离限制的简单版:BZOJ 1767 [Ceoi2009]harbingers 定义$DP[i]$为从i出发到1号点的最小花费,$dis_i$为i到1号点的距离: 转移: $DP[i]=min(DP[j]+(dis_i-dis_j)P_j)+Q_i$ $\quad=min(DP[j]-dis_jP_i)+dis_iP

【BZOJ 3672】【UOJ #7】【NOI 2014】购票

http://www.lydsy.com/JudgeOnline/problem.php?id=3672 http://uoj.ac/problem/7 链上的情况可以用斜率优化dp.树上用斜率优化dp时,单调队列的复杂度是均摊$O(n)$的,所以放到树上做“可持久化单调队列”复杂度是$O(n^2)$的,所以不能树上斜率优化. 这道题可以用树链剖分(时间复杂度$O(nlog^3n)$)或者点分治套cdq分治(时间复杂度$O(nlog^2n)$).因为树链剖分感觉比较难写,而且每个节点用vecto

BZOJ 3672 NOI2014 购票

这题好神啊..好神啊...好神啊... 首先列出N2的DP方程较易. 从DP方程很容易看出来是斜率优化. 如何进一步优化? 考虑对当前点以上的链建立一个下凸包. 维护凸包就可以,但不是很好写. 观察到方程可以必然由它的祖先节点转移.很像Cash那道题. 尝试CDQ分治,每次先递归处理根所在的子树. 然后用根所在的子树,对当前点更新答案,对其他点进行根据dis-lim排序,维护栈即可. 考虑到复杂度,我们需要对树进行点分治. code: #include<iostream> #include&l

BZOJ 3672 NOI2014 购票 树的点分治+斜率优化

题目大意:给定一棵以1为根的有根树,每条边有边权,每个点有三个值pi,qi,li 从一个点可以走到它的某个祖先处,前提是距离d不超过li,花销为pi*d+qi 求从每个点到达根节点的最小花销 这道题的上一份题解:http://blog.csdn.net/popoqqq/article/details/39009219 很不幸我作死去重写了一发233 之前的写法真是SB的1B... 为何要暴力- - 明明是分治结构直接排序不行么- - 简述一下做法: 0.先推出斜率优化的动归方程 1.找到当前分治

bzoj 3672 利用点分治将CDQ分治推广到树型结构上

最大的收获就是题目所说. deal(s) : 处理节点s所在块的问题,并保证: 1.s是该块中最靠近根节点的点,没有之一. 2.s所在块到根节点的路径上的点全都用来更新过了s所在块的所有节点. 然后步骤是: 1.找s所在块的重心c. 2.如果s就是c,那么用c更新当前块的所有节点,然后“删除c”,递归处理新产生的子块. 3.否则,删除c,deal(s),用c到s的路径(不包括c,包括s)更新c除了s子块的其他子块以及c,然后再用c去更新一次. 4.递归处理其它子块. 1 #include <cs

记录 [补档]

数学 概率论 计数问题 数论 线性代数 博弈论 比赛经验 不要通过数据大小猜测正解的时间复杂度. 把一个方法想到底. DP题假如实在不会的话, 果断跳过, 找思维量更小的数据结构题. 一些方法 二分 DP, 尤其多想矩阵乘法优化DP 网络流 FFT 日程表 Fri, Nov 10 明天就是NOIp了. 从上次记录到现在已经将近一个月了, 这个月, 真心没有进步多少, 完全不再状态. 希望不要AFO吧. 这可能是最后一篇记录了. Sun, Oct 22 AHOI 2009 中国象棋: 统计在棋盘上

NOI2014题解

起床困难综合症(BZOJ 3668) 送分题,直接从高位向低位贪心. 魔法森林(BZOJ 3669) 一个容易想到的办法就是枚举A的最大值,以B作为权值求最小生成树.暴力的话要T的.如果从小到大枚举A的最大值,每次仅会添加一些新的边,所以自然想到用LCT维护.本来还可以考一下LCT的,结果考场上A此题的人多半都写的各式各样的黑暗算法.真不知道出题人是怎么出的数据. 动物园(BZOJ 3670) 题目里面就把题解都说明白了,要用KMP.求了next数组后,以next为边建成一棵树,然后就很好做了,

【BZOJ-3672】购票 树分治 + 斜率优化DP

3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1177  Solved: 562[Submit][Status][Discuss] Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.