题解[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\)\(x\) 的贡献,则有 \(dfn_y+1\leq dfn_x\leq dfn_y+sz_y-1\),自然想到线段树分治。

  • 按照 \(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');
}
posted @ 2021-11-11 22:42  Y_B_X  阅读(25)  评论(0编辑  收藏  举报