題解[P2305 購票]
題意:
給定一棵樹,每個點到其父親距離為 \(s_i\)。
每個點能從到此點距離不超過 \(l_i\) 的祖先跳過來。
代價是 \(d\times p_i+q_i\),其中 \(d\) 為到祖先的距離。
求每個點從根節點跳過來的最小代價 \(f_i\)。
\(n\leq 2\times 10^5\)
以下說明中 \(u_x=p_x,v_x=q_x\),\(dis_x\) 為 \(x\) 到根的距離和。
\[f_x=\mathop{\mathrm{Min}}\limits_{x\in \operatorname{subtree}(y)}[dis_x-dis_y\leq l_x]\Big(f_y+(dis_x-dis_y)u_x+v_x\Big) \]\[f_x=dis_xu_x+v_x+\mathop{\mathrm{Min}}\limits_{x\in\operatorname{subtree}(y)}\big[(-dis_y)\leq tl_x\big]\Big(f_y+u_x(-dis_y)\Big) \]\[\Big[tl_x=l_x-dis_x\Big],\begin{cases}\mathrm{X}_y=-dis_y\\\mathrm{Y}_y=f_y\\\mathrm{K}_x=-u_x\end{cases} \]-
考慮每個 \(y\)
-
按照 \(dfs\) 序處理每一個點,用線段樹將上面的區間限制拆成幾個區間,將這些區間內加上點 \((\operatorname{X}_y,\operatorname{Y}_y)\) 。
-
這可以在處理到線段樹上的葉子結點時,保證了其 \(f\) 值已求出,可對其子樹區間動態加入 \((\operatorname{X}_y,\operatorname{Y}_y)\)。
-
中序遍歷整顆線段樹,將區間被加上的點按 \(\operatorname{X}\) 排序後,維護一個下凸包,對區間內的每個點找出最優決策點。
但同時有 \(\operatorname{X}_y\leq tl_x\) 的限制。
- 那就應該對區間內的 \(tl_x\) 排序,依次加進滿足條件的 \(\operatorname{X}_y\),再對每個 \(\operatorname{K}_x\) 在凸包上二分得到最優決策點。
而每個點線上段樹上會被其祖先更新 \(O(\log n)\) 次,每次二分也是 \(O(\log n)\),總時間是 \(O(n\log^2 n)\)。
-
由於排序只基於 \(\operatorname{X},\operatorname{K},tl\),在預處理完之後成為定值,在處理之前先按 \(\operatorname{X}_y,\operatorname{K}_x\)
-
一個點被貢獻的一定是其父節點,題目資料範圍能保證其 \(dis\) 互不相同,不必擔心凸包的 \(\operatorname{Y}\) 座標。
程式碼:(卡精度,要開 \(\text{long double}\))
#include<bits/stdc++.h>
#define ll long long
#define db long double
using namespace std;
const ll inf=4e18;
const int N=2e5+10,M=5e6+10;
int n,m,x,y,tt,xx,yy,l_,r_,tot,pl,pr,pmid,pans;char ch;
int to[N],nextn[N],h[N],edg,dfn[N],sz[N],rev[N];
inline void add(int x,int y){to[++edg]=y,nextn[edg]=h[x],h[x]=edg;}
ll u[N],v[N],dis[N],tl[N],l[N],w[N],f[N];
inline void read(int &x){
x=0;ch=getchar();while(ch<47)ch=getchar();
while(ch>47)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
inline void read(ll &x){
x=0;ch=getchar();while(ch<47)ch=getchar();
while(ch>47)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
void write(ll x){if(x>9)write(x/10);putchar(48+x%10);}
struct node{
int i;ll x,tl;node()=default;
node(int _i,ll _x,ll _tl):i(_i),x(_x),tl(_tl){}
}a[N];
bool cmpx(node &a,node &b){return a.x>b.x;}
bool cmptl(node &a,node &b){return a.tl>b.tl;}
void init(int x,int anc){
int i,y;rev[dfn[x]=++tt]=x;dis[x]+=dis[anc];
tl[x]=l[x]-dis[x];w[x]=dis[x]*u[x]+v[x];sz[x]=1;
for(i=h[x];y=to[i];i=nextn[i])init(y,x),sz[x]+=sz[y];
}
int toq[M],nextnq[M],hq[N<<2],edgq;
int tos[M],nextns[M],hs[N<<2],edgs;
int tor[M],nextnr[M],hr[N<<2],edgr;
inline void addq(int x,int y){toq[++edgq]=y,nextnq[edgq]=hq[x],hq[x]=edgq;}
inline void addr(int x,int y){tor[++edgr]=y,nextnr[edgr]=hr[x],hr[x]=edgr;}
#define ls k<<1
#define rs k<<1|1
void updateq(int k,int l,int r){
if(xx<=l&&r<=yy)addq(k,x);
else {
int mid=(l+r)>>1;
if(xx<=mid)updateq(ls,l,mid);
if(mid<yy)updateq(rs,mid+1,r);
}
}
void updater(int k,int l,int r,int pos){
addr(k,x);
if(l^r){
int mid=(l+r)>>1;
if(pos<=mid)updater(ls,l,mid,pos);
else updater(rs,mid+1,r,pos);
}
}
struct point{
db x,y;point()=default;point(db _x,db _y):x(_x),y(_y){}
inline bool operator <=(const point &a)const {return y*a.x<=x*a.y;}
point operator -(const point &a)const {return point(x-a.x,y-a.y);}
}p[N],t[N],tmp;
bool bb[N];
bool dzt;
void solve(int k,int l,int r){
int i,y;
if(hr[k]){
dzt=(l==6)&&(r==9);
r_=tot=0;
for(i=hq[k];y=toq[i];i=nextnq[i]){
tmp=point(-dis[y],f[y]);t[++tot]=tmp;
while(r_>1&&(tmp-p[r_])<=(p[r_]-p[r_-1]))--r_;
p[++r_]=tmp;
}
int j=0;
r_=0;
for(i=hr[k];y=tor[i];i=nextnr[i]){
while(t[j+1].x<=tl[y]&&j<tot){
tmp=t[++j];
while(r_>1&&(tmp-p[r_])<=(p[r_]-p[r_-1]))--r_;
p[++r_]=tmp;
}
tmp=point(1,-u[y]);
if(r_>1){
pl=2,pr=r_;pans=0;
while(pl<=pr){
pmid=(pl+pr)>>1;
if((p[pmid]-p[pmid-1])<=tmp)pans=pmid,pl=pmid+1;
else pr=pmid-1;
}
if(!pans)pans=1;
}
else pans=r_;
if(pans)f[y]=min(f[y],(ll)p[pans].y+u[y]*(ll)p[pans].x);
}
}
if(l^r){
int mid=(l+r)>>1;
solve(ls,l,mid);
solve(rs,mid+1,r);
}
else y=rev[l],f[y]+=w[y];
}
main(){
read(n);read(x);register int i;
for(i=2;i<=n;++i){
read(x);add(x,i);f[i]=inf;
read(dis[i]);read(u[i]);
read(v[i]);read(l[i]);
}
init(1,0);
for(i=1;i<=n;++i)a[i]=node(i,-dis[i],tl[i]);
sort(a+1,a+n+1,cmpx);
for(i=1;x=a[i].i,i<=n;++i)if(sz[x]^1){
xx=dfn[x]+1,yy=dfn[x]+sz[x]-1;
updateq(1,1,n);
}
sort(a+1,a+n+1,cmptl);
for(i=1;x=a[i].i,i<=n;++i)updater(1,1,n,dfn[x]);
solve(1,1,n);
for(i=2;i<=n;++i)write(f[i]),putchar('\n');
}