●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_i+Q_i$

显然这个$O(N^2)$的转移会超时。



考虑优化:

假设对于当前计算的DP[i],有两个转移来源点:k,j,同时dis[k]<dis[j],设j点比k点优。

那么有:$DP[j]-dis_j*P_i-(DP[k]-dis_k*P_i)<0$

则: $\frac{DP[j]-DP[k]}{dis[j]-dis[k]}<P[i]$

如果令 Slope(j,k)=$\frac{DP[j]-DP[k]}{dis[j]-dis[k]}$

那么得到结论,如果 $dis_k<dis_j$,且Slope(j,k)<P[i]的话,则j点优于k点。

同时如果存在三个转移来源点:k,j,i,满足$dis_k<dis_j<dis_i$,

同时Slope(i,j)<Slope(j,k),则j点无效。

所以对于每个来源点二元组(dis[j],DP[j]),只需要在平面上维护一个下凸壳即可。



1).如果问题不在一颗树上,而是在一个序列上,这个应该比较容易吧:一个裸的CDQ就可以解决了。

2).而如果问题没有limit这个距离限制,即使在树上,也比较容易了,因为随着DFS遍历树时,dis是单增的,所以可以直接维护单调栈(详细见BZOJ 1767 [Ceoi2009]harbingers

但是现在既在树上又有距离限制怎么办呢?这里采用点分治来实现CDQ分治的功能,(可以叫做树上CDQ分治么)

对于当前的子树,我们令根为u,(这个根是子树内的点在前往1号点时都要经过的地方)。

并找到子树内的重心cg(the center of gravity),

然后先递归处理cg为根时含有u的那颗子树,

不难发现,cg的其它子树的点(令这些点的集合为R)在前往1号点时,都会进过cg~u这一条链,

即这条链上的点可能会成为R里的点的转移点。

同时为了满足距离这一限制,即每个点i最多只能向上到达 $dis_i-limit_i$这个位置

所以把R里的点按 $dis_i-limit_i$排序后,从大到小枚举R里的i点,并把cg~u这条链上dis小于$dis_i-limit_i$的点用单调栈维护一个下凸壳。

接着在凸壳里二分最优的转移来源点即可。

整个过程是$O(Nlog_2^2N)$的。

(伤不起,读入居然要long long!)

代码:PoPoQQQ的代码写得很棒呀!

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 200050
#define ll long long
using namespace std;
ll DP[MAXN],P[MAXN],Q[MAXN],G[MAXN],dis[MAXN];
int fa[MAXN],siz[MAXN];
int N,t;
struct Edge{
	ll val[MAXN];
	int to[MAXN],nxt[MAXN],head[MAXN],ban[MAXN],ent;
	Edge(){ent=2;}
	void Adde(int u,int v,ll w){
		to[ent]=v; val[ent]=w; nxt[ent]=head[u]; head[u]=ent++;
	}
}E;
struct Mostk{
	int s[MAXN],top;
	#define Slope(i,j) (1.0*(DP[i]-DP[j])/(dis[i]-dis[j]))
	void Reset(){top=0;}
	void Push(int i){
		if(top&&dis[s[top]]==dis[i])
			{if(DP[i]<DP[s[top]]) top--; else return;}
		while(top>1&&Slope(s[top-1],s[top])<Slope(s[top],i)) top--;
		s[++top]=i;
	}
	int Query(int i){//二分单调栈
		static int l,r,mid,ret;
		if(!top) return 0;
		l=2; r=top; ret=1;
		while(l<=r){
			mid=(l+r)>>1;
			if(Slope(s[mid-1],s[mid])>=P[i]) ret=mid,l=mid+1;
			else r=mid-1;
		}
		return s[ret];
	}
}S;
void dfs(int u){
	for(int i=E.head[u];i;i=E.nxt[i]){
		int v=E.to[i];
		dis[v]=dis[u]+E.val[i];
		dfs(v);
	}
}
void getcg(int u,int &cg,int &num,int sum){
	int maxnum=0; siz[u]=1;
	for(int i=E.head[u];i;i=E.nxt[i]){
		int v=E.to[i]; if(E.ban[i]) continue;
		getcg(v,cg,num,sum);
		maxnum=max(maxnum,siz[v]);
		siz[u]+=siz[v];
	}
	maxnum=max(maxnum,sum-siz[u]);
	if(maxnum<=num) num=maxnum,cg=u;
}
void trave(int u,int &cr,int *R){
	R[++cr]=u;
	for(int i=E.head[u];i;i=E.nxt[i]){
		if(E.ban[i]) continue;
		trave(E.to[i],cr,R);
	}
}
bool cmp(int i,int j){
	return dis[i]-G[i]>dis[j]-G[j];
}
void solve(int u,int num){
	static int R[MAXN],cr,maxnum;
	if(num==1) return;
	int cg=u; maxnum=num;
	getcg(u,cg,maxnum,num);
	//-----------------------------准备递归u区域
	for(int i=E.head[cg];i;i=E.nxt[i]) E.ban[i]=1;
	solve(u,num-siz[cg]+1);
	//-----------------------------开始解决当前层的贡献
	cr=0; S.Reset();
	for(int i=E.head[cg];i;i=E.nxt[i]) trave(E.to[i],cr,R);
	sort(R+1,R+cr+1,cmp);
	for(int i=1,j=cg,k;i<=cr;i++){
		while(j!=fa[u]&&dis[j]>=dis[R[i]]-G[R[i]]) S.Push(j),j=fa[j];
		k=S.Query(R[i]);
		if(k) DP[R[i]]=min(DP[R[i]],DP[k]+(dis[R[i]]-dis[k])*P[R[i]]+Q[R[i]]);
	}
	//-----------------------------递归解决剩下子树区域
	for(int i=E.head[cg];i;i=E.nxt[i])
		solve(E.to[i],siz[E.to[i]]);
}
void read(ll &x){
	static int sn; static char ch;
	x=0; sn=1; ch=getchar();
	while(ch<‘0‘||‘9‘<ch){if(ch==‘-‘)sn=-1;ch=getchar();}
	while(‘0‘<=ch&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
	x=x*sn;
}
int main(){
	scanf("%d%d",&N,&t);
	memset(DP,0x3f,sizeof(DP));
	DP[1]=0; ll f,s,p,q,g;
	for(int i=2;i<=N;i++){
		read(f); read(s); read(p); read(q); read(g);
		fa[i]=f; P[i]=p; Q[i]=q; G[i]=g;
		E.Adde(f,i,s);
	}
	dfs(1);
	solve(1,N);
	for(int i=2;i<=N;i++) printf("%lld\n",DP[i]);
	return 0;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8168827.html

时间: 2024-10-25 06:16:34

●BZOJ 3672 [Noi2014]购票的相关文章

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 购票

这题好神啊..好神啊...好神啊... 首先列出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 购票 点分治+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 的整数编号.

【BZOJ3672】[Noi2014]购票 树分治+斜率优化

[BZOJ3672][Noi2014]购票 Description 今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会. 全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接.为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号.其中SZ市的编号为 1.对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 fv  以及到父亲城市道路的长度 sv. 从城市 v 前往SZ市的方法为:选择城

【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 3669: [Noi2014]魔法森林

3669: [Noi2014]魔法森林 Time Limit: 30 Sec  Memory Limit: 512 MB 动点spfa Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节点1,隐士则住在号节点N.小E需要通过这一片魔法森林,才能够拜访到隐士. 魔法森林中居住了一些妖怪.每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击.幸运的

【bzoj3672】[Noi2014]购票 斜率优化+CDQ分治+树的点分治

题目描述 给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费的最小代价(中途可以经停其它点) 输入 第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到).输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市.其中第 v 行包含 5 个非负整数 $f_v,s_v,p_v,q_v,l_v$,分别表示城市 v 的父亲城市,它到

BZOJ 3669: [Noi2014]魔法森林( LCT )

排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) -------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const int maxn = 1