#1992. 购票(ticket)

题目描述

题解

考虑啥限制都没有的话,也就是在一个序列上且没有 l l l 的限制,那就是 d p dp dp 然后斜率优化

f i f_i fi 表示 i i i 的答案, f i = m i n ( f j + ( d i − d j ) p i + q i ) f_i=min(f_j+(d_i-d_j)p_i+q_i) fi=min(fj+(didj)pi+qi) ,其中 d i d_i di 表示 s s s 的前缀和,可以化成斜率优化的式子

由于斜率不递增所以在凸包上二分即可

考虑 l l l 的限制的话,那我们可以做类似 c d q cdq cdq 的东西,也就是考虑 [ l , m i d ] [l,mid] [l,mid] [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的影响,那可以对右区间以最前能取到的点从大到小排序,然后双指针加入凸包即可

考虑在一棵树上的话,那我们考虑点分治,即对于树 u u u 求出了重心 r t rt rt ,考虑 ( u , r t ) (u,rt) (u,rt) 路径上的点对 r t rt rt 子树的影响,那我们可以先除去 r t rt rt 子树内的点(除了 r t rt rt ),然后递归求出剩下点的 d p dp dp 值,然后我们可以像刚刚的操作即把 r t rt rt 子树内除了 r t rt rt 的点按照最高能取到的点按深度从大到小排序,然后双指针加入凸包即可

效率: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码
#include <bits/stdc++.h>
#define LL long long
#define db long double
using namespace std;
const int N=2e5+5;
int n,fa[N],o,rt,sz[N],son[N],a[N],hd[N],V[N],nx[N],t,S[N],tp;
LL d[N],s[N],p[N],q[N],g[N],f[N];bool vis[N];
bool cmp(int A,int B){
	return d[A]-g[A]>d[B]-g[B];
}
void add(int u,int v){
	nx[++t]=hd[u];V[hd[u]=t]=v;
}
db K(int x,int y){
	return ((db)f[y]-f[x])/((db)d[y]-d[x]);
}
void Dis(int x){
	for (int i=hd[x];i;i=nx[i])
		d[V[i]]=d[x]+s[V[i]],Dis(V[i]);
}
void Sz(int x){
	sz[x]=1;
	for (int i=hd[x],v;i;i=nx[i])
		if (!vis[v=V[i]])
			Sz(v),sz[x]+=sz[v];
}
void Rt(int x){
	son[x]=o-sz[x];
	for (int i=hd[x],v;i;i=nx[i])
		if (!vis[v=V[i]])
			Rt(v),son[x]=max(son[x],sz[v]);
	if (son[x]<=son[rt]) rt=x;
}
void find(int x){
	a[++t]=x;
	for (int i=hd[x];i;i=nx[i])
		if (!vis[V[i]]) find(V[i]);
}
void work(int x){
	Sz(x);o=sz[x];rt=0;Rt(x);
	if (o==1){vis[x]=1;return;}
	for (int i=hd[rt];i;i=nx[i]) vis[V[i]]=1;
	int u=rt;work(x);tp=t=0;
	for (int i=hd[u];i;i=nx[i])
		vis[V[i]]=0,find(V[i]);
	sort(a+1,a+t+1,cmp);int X=x;x=u;
	for (int i=1,v,l,r,j;i<=t;i++){
		v=a[i];
		while(d[u]>=d[X] && d[u]>=d[v]-g[v]){
			while(tp>1 && K(S[tp-1],u)>=K(S[tp-1],S[tp])) tp--;
			S[++tp]=u;u=fa[u];
		}
		if (!tp) continue;l=1;r=tp;
		while(l<r){
			j=(l+r+1)>>1;
			if (K(S[j],S[j-1])>=p[v]) l=j;
			else r=j-1;
		}
		f[v]=min(f[v],f[S[l]]+(d[v]-d[S[l]])*p[v]+q[v]);
	}
	vis[x]=1;
	for (int i=hd[x];i;i=nx[i]) work(V[i]);
}
int main(){
	cin>>n>>tp;son[0]=1e9;d[0]=-1;
	for (int i=2;i<=n;i++)
		scanf("%d%lld%lld%lld%lld",
		&fa[i],&s[i],&p[i],&q[i],&g[i]),
		f[i]=2e18,add(fa[i],i);
	Dis(1);work(1);
	for (int i=2;i<=n;i++)
		printf("%lld\n",f[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值