[NOI2014]购票

题面在这里

题意

给出一棵大小为(n)的有根树,每个节点的父亲(fv)以及父边长(sv)
每个节点给出一个距离限制(lv)。对于(v)的祖先(a)
只有当它们之间所有道路的总长度不超过(lv)时,
从城市(v)才可以通过一次购票到达城市(a),否则不能通过一次购票到达。
对于每个城市(v),还给出两个非负整数(pv,qv)
若城市(v)到城市(a)所有道路的总长度为(d)
那么从城市(v)到城市(a)购买的票价为(d imes pv+qv)
求每个节点到达根节点的最小代价

数据范围

[nle2 imes10^5 ]

sol

NOIday2压轴题。

假设我们面对的只有一条链,那么设(f[i])表示第(i)号节点的答案,(s[i])为第(i)号节点到第(j)号点的距离,那么有

[f[i]=min_{j=fa[i]}^{s[i]-s[j]le l[i]}(f[j]+(s[i]-s[j]) imes p[i]+q[i])]

[=min_{j=fa[i]}^{s[i]-s[j]le l[i]}(f[j]-s[j] imes p[i])+s[i] imes p[i]+q[i] ]

插点((s[j],f[j])),询问(k_i=p[i]);
然后我们需要面对两个问题:

树上操作

这个需要维护一个可持久化单调队列,具体请看这里;

处理横坐标连续的一些点的凸包

使用单调队列的时候,直接弹出凸包肯定是不对的
那么我们肯定需要数据结构来支持合并下凸包
其实合并下凸包没有想象中那么难,
直接把斜率在每个下凸包中取到的最值合并即可

插入和回溯时我们每次需要更新线段树上的(O(logn))个节点,
每个点最多在每个线段树节点上进入两次出去两次(可持久化),

询问答案时我们每次二分距离是(O(logn))的,
之后凸包上二分是(O(logn))的,线段树区间合并是(O(logn))的,
所以总时间就是(O(n(logn+logn+logn+log^2n)))?(可能需要卡常)

那么很明显这就是一道用数据结构维护的计算几何题(雾

代码

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pub push_back
#define puf push_front
#define pob pop_back
#define pof pop_front
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const dd eps=1e-10;
const int mod=1e8;
const int N=200010;
const ll inf=1e18;
il ll read(){
	RG ll data=0,w=1;RG char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
	return data*w;
}

il void file(){
	freopen("b.in","r",stdin);
	freopen("a.out","w",stdout);
}

ll n,T,f[N],s[N],p[N],q[N],l[N];
int head[N],nxt[N<<1],to[N<<1],cnt;
ll val[N<<1];
il void add(int u,int v,ll w){
	to[++cnt]=v;
	val[cnt]=w;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

int cal[20][N],top[20];
ll calx[20][N],caly[20][N];
#define ls (i<<1)
#define rs (i<<1|1)
#define mid ((l+r)>>1)
il dd getk(ll ax,ll ay,ll bx,ll by){
	if(ax==bx)return inf;return (ay-by)*1.0/(ax-bx);
}
struct node{
	vector<ll> qx,qy;
	il void init(){qx.clear();qy.clear();}
	il int check(ll k,int x){
		if(x!=qx.size()-1&&k>getk(qx[x+1],qy[x+1],qx[x],qy[x]))
			return 1;
		if(x&&k<getk(qx[x],qy[x],qx[x-1],qy[x-1]))
			return -1;
		return 0;
	}
	il ll solve(ll k){
		RG int l=0,r=qx.size()-1,ret;
		while(l<=r){
			ret=check(k,mid);
			if(!ret)return qy[mid]-k*qx[mid];
			else if(ret==1)l=mid+1;
			else r=mid-1;
		}
	}
}t[20*N];

il void build(int i,int l,int r){
	t[i].init();if(l==r)return;
	build(ls,l,mid);build(rs,mid+1,r);
}

il void modify(int i,ll x,ll y,int dep,int num){
	RG int sz=t[i].qx.size();
	while(sz>=2&&getk(t[i].qx[sz-1],t[i].qy[sz-1],t[i].qx[sz-2],t[i].qy[sz-2])>=getk(t[i].qx[sz-1],t[i].qy[sz-1],x,y)){
		top[dep]++;
		cal[dep][top[dep]]=num;
		calx[dep][top[dep]]=t[i].qx[sz-1];
		caly[dep][top[dep]]=t[i].qy[sz-1];
		t[i].qx.pop_back();t[i].qy.pop_back();sz--;
	}
	t[i].qx.push_back(x);t[i].qy.push_back(y);
}
void inserttree(int i,int l,int r,int pos,ll x,ll y,int dep,int num){
	modify(i,x,y,dep,num);
	if(l==r)return;
	if(pos<=mid)inserttree(ls,l,mid,pos,x,y,dep+1,num);
	else inserttree(rs,mid+1,r,pos,x,y,dep+1,num);
}

ll querytree(int i,int l,int r,int x,int y,ll k){
	if(x<=l&&r<=y)return t[i].solve(k);
	RG ll s=inf;
	if(x<=mid)s=querytree(ls,l,mid,x,y,k);
	if(y>mid)s=min(s,querytree(rs,mid+1,r,x,y,k));
	return s;
}
VI now;
il ll query(ll dist,ll k){
	RG int l=0,r=now.size()-1,ans;
	while(l<=r){
		if(s[now[mid]]>=dist)
			ans=mid,r=mid-1;
		else l=mid+1;
	}
	return querytree(1,1,n,ans+1,now.size()-1,k);
}

void canceltree(int i,int l,int r,int u,int pos,int dep){
	if(l!=r){
		if(pos<=mid)canceltree(ls,l,mid,u,pos,dep+1);
		else canceltree(rs,mid+1,r,u,pos,dep+1);
	}

	t[i].qx.pop_back();t[i].qy.pop_back();
	while(cal[dep][top[dep]]==u){
		t[i].qx.push_back(calx[dep][top[dep]]);
		t[i].qy.push_back(caly[dep][top[dep]]);
		top[dep]--;
	}
	
}

void dfs_DP(int u,int fa){
	now.push_back(u);
	if(u!=1)f[u]=query(s[u]-l[u],p[u])+s[u]*p[u]+q[u];
	inserttree(1,1,n,now.size(),s[u],f[u],1,u);
	for(RG int i=head[u];i;i=nxt[i]){
		RG int v=to[i];if(v==fa)continue;
		s[v]=s[u]+val[i];dfs_DP(v,u);
	}
	canceltree(1,1,n,u,now.size(),1);now.pop_back();
}

int main()
{
	n=read();T=read();
	for(RG int i=2,fa,w;i<=n;i++){
		fa=read();w=read();add(i,fa,w);add(fa,i,w);
		p[i]=read();q[i]=read();l[i]=read();
	}
	build(1,1,n);
	dfs_DP(1,0);
	for(RG int i=2;i<=n;i++)printf("%lld
",f[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/cjfdf/p/8672879.html