码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj-3672 购票

时间:2015-08-31 19:45:24      阅读:192      评论:0      收藏:0      [点我收藏+]

标签:bzoj   树的点分治   斜率优化   

题意:

给出一颗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;
}



bzoj-3672 购票

标签:bzoj   树的点分治   斜率优化   

原文地址:http://blog.csdn.net/ww140142/article/details/48136313

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!