题面在这里
题意
给出一棵大小为(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;
}