[NOI2014] 购票 题解

首先发现 \(p_x\times dis(x,y)+q_x\) 异常像是能斜率优化的样子,那先把求 \(f_x\) 的式子写出来(下设 \(d_x\) 表示 \(x\) 到根的距离):

\[f_x=\min_{lca(x,y)=y,y\ne x}(p_x\times(d_x-d_y)+q_x+f_y) \]

提出公共部分 \(p_x\times d_x+q_x\),得:

\[f_x=p_x\times d_x+q_x+\min_{lca(x,y)=y,y\ne x}(f_y-p_x\times d_y) \]

那当 \(y\) 优于 \(z\) 时(设 \(d_y<d_z\)),一定满足:

\[f_y-p_x\times d_y<f_z-p_x\times d_z \]

\[f_y-f_z<p_x\times(d_y-d_z) \]

\[\dfrac{f_y-f_z}{d_y-d_z}>p_x \]

那这里就形成了一个斜率优化 \(dp\) 的式子,得用单调栈维护凸包。

但是这里就出现了一个很现实的问题:实际上你要维护的是多个滑动窗口内的凸包,也就意味着你原来淘汰的点可能在后面的决策中再次出现!

问题转化为维护后缀凸包,考虑树状数组维护单调栈,这样就能在 \(O(n\log^2n)\) 的时间中解决问题。

最后发现由于是在树上,所以要支持可撤销,但是明显的,单调栈的时间复杂度是均摊的,单次修改仍然有可能达到 \(O(n)\),所以单调队列插入时,应当使用二分找到插入点,保证单次时间复杂度 \(O(\log n)\)

时间复杂度 \(O(n\log^2n)\)。注意单调栈要用 \(vector\)

#include<bits/stdc++.h>
#define int long long
#define db double
using namespace std;
const int N=2e5+5,M=4e6+5;
struct del{
	int id,tp,ps;
}sk[M];vector<int>g[N];
int n,m,idx[N],ln[N],ft;
int d[N],p[N],q[N],f[N];
struct mstack{
	int tp=-1;vector<int>st;
	db sp(int x,int y){
		return 1.0*(f[x]-f[y])/(d[x]-d[y]);
	}int cmp(int x,int y,int z){
		return sp(x,y)>=sp(y,z);
	}void add(int x,int id){
		int l=1,r=tp,ans=tp+1;
		while(l<=r){
			int mid=(l+r)/2;
			if(cmp(st[mid-1],st[mid],x))
				ans=mid,r=mid-1;
			else l=mid+1;
		}if(ans>tp) st.push_back(0);
		sk[++ft]={id,tp,st[ans]},st[tp=ans]=x; 
	}int ans(int x){
		if(tp<0) return (int)9e18;
		int l=0,r=tp-1,ans=st[tp];
		while(l<=r){
			int mid=(l+r)/2;
			if(sp(st[mid],st[mid+1])>x)
				ans=st[mid],r=mid-1;
			else l=mid+1;
		}return f[ans]-d[ans]*x;
	}
};namespace BIT{
	mstack c[N];
	void clear(int lft){
		while(ft>lft){
			del x=sk[ft--];
			c[x.id].st[c[x.id].tp]=x.ps;
			c[x.id].tp=x.tp;
		}
	}void add(int x,int v){
		x=n-x+1;
		for(;x<=n;x+=x&-x)
			c[x].add(v,x);
	}int ans(int x,int v){
		int mn=9e18;x=n-x+1;
		for(;x;x-=x&-x)
			mn=min(mn,c[x].ans(v));
		return mn;
	}
}using namespace BIT;
void dfs(int x,int fa){
	idx[++m]=(d[x]+=d[fa]);
	int cc=lower_bound(idx+1,idx+m+1,d[x]-ln[x])-idx,lft=ft;
	if(x>1) f[x]=d[x]*p[x]+q[x]+ans(cc,p[x]);add(m,x);
	for(auto y:g[x]) dfs(y,x);clear(lft),m--;
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>ft,ft=0;
	for(int i=2;i<=n;i++){
		int fa;cin>>fa>>d[i]>>p[i]>>q[i];
		cin>>ln[i],g[fa].push_back(i);
	}dfs(1,0);
	for(int i=2;i<=n;i++)
		cout<<f[i]<<"\n";
	return 0;
}
posted @ 2024-12-23 08:59  长安一片月_22  阅读(8)  评论(0编辑  收藏  举报