P2305 [NOI2014] 购票

考虑动态规划:
设置状态 \(f_i\) 表示从 \(i\)\(1\) 所需的最少资金。
则有状态转移(\(j\)\(i\) 的祖先): \(f_i = \min_{d_i-d_j \leq l_i}{f_j + (d_i-d_j)\cdot p_i+q_i}\).
对式子进行简化,变为 \(f_i = \min_{d_i-d_j \leq l_i}{(f_j-d_j\cdot p_i)}+d_i\cdot p_i + q_i\).

  • 若不考虑距离限制,则可以用李超线段树协助转移,时间复杂度 \(\mathcal{O(n\log n)}.\)
  • 考虑一条链的情况,此时加上距离限制,可以用线段树套李超来解决,时间复杂度 \(\mathcal{O(n \log ^2 n)}\)

由以上两个特殊情况,推到普遍情况。
考虑运用出栈序解决树的情况,出栈序即离开每个节点返回祖先的顺序。
我们可以发现出栈序的特殊性质:

对于一个点 \(u\),它的祖先表示为 \(fa\),则 \(dfn_u\)\(dfn_{fa}\) 之间的所有点,要不没遍历过,要不就是 \(u\)\(fa\) 路径上的点。

这个特殊性质很容易理解,画一画图就可以了。
于是就可以转化为链的情况,用树套树解决问题。

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair

using namespace std;

const int N = 2e5+5;

inline ll read() {
    ll x = 0; char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = x*10 + c-'0', c = getchar();
    return x;
}
namespace LC {
	struct segment{
		ll k,b;
		int p;
		ll operator ()(ll x){
			return k*x+b;
		}
	}tr[N*20];
	int ls[N*20],rs[N*20];
	int idx=0;
	void Ins(int &u,int l,int r,segment t){
		if(!u){
			u=++idx;
			tr[u]=t;
			return ;
		}
		int mid=l+r>>1;
		if(t(mid)<tr[u](mid))swap(tr[u],t);
		if(l>=r)return ;
		if(t.k<tr[u].k) Ins(rs[u],mid+1,r,t);
    	else Ins(ls[u],l,mid,t);
	}
	ll qry(int u,int l,int r,int pos){
		if(!u)return 1e18;
		ll res=tr[u](pos);
		int mid=l+r>>1;
		if(pos<=mid)res=min(res,qry(ls[u],l,mid,pos));
		else res=min(res,qry(rs[u],mid+1,r,pos));
		return res;
	}
}
struct Edge{
	int to,nxt;
}e[N];

int tot=0,head[N];
void add_Edge(int u,int v){
	e[tot].nxt=head[u];
	e[tot].to=v;
	head[u]=tot++;
}
int n,m,top;
int fa[N];
int stk[N],b[N];
ll d[N],f[N],s[N],l[N],p[N],q[N];

#define ls u<<1
#define rs u<<1|1

struct Tree{
	int rt;
}tr[N<<2];

void update(int u,int l,int r,int pos,LC::segment t){
	LC::Ins(tr[u].rt,0,1e6,t);
	if(l==r)return ;
	int mid=l+r>>1;
	if(pos<=mid)update(ls,l,mid,pos,t);
	else update(rs,mid+1,r,pos,t);
}

ll query(int u,int l,int r,int L,int R,int x){
	if(L<=l && r<=R)return LC::qry(tr[u].rt,0,1e6,x);
	int mid=l+r>>1;
	if(L>mid)return query(rs,mid+1,r,L,R,x);
	if(R<=mid)return query(ls,l,mid,L,R,x);
	return min(query(ls,l,mid,L,R,x),query(rs,mid+1,r,L,R,x));
}
void dfs(int u) {
//	cout<<u<<"\n";
	for(int i=head[u];~i;i=e[i].nxt) {
		int v=e[i].to;
		dfs(v);
	}
	b[u]=++top;
} 

void DFS(int u){
	LC::segment t={-d[top],f[u],u};
	stk[top]=u;
	update(1,1,n,b[u],t);
//	cout<<b[u]<<"\n";
	for(int i=head[u];~i;i=e[i].nxt) {
		int v=e[i].to;
		top++;
		d[top]=d[top-1]+s[v];
		int x=lower_bound(d,d+top,d[top]-l[v])-d;
	//	cout<<d[top]-l[v]<<'\n';
		int ql=b[v],qr=b[stk[x]];
	//	cout<<ql<<" "<<qr<<"\n";
		f[v]=query(1,1,n,ql,qr,p[v])+d[top]*p[v]+q[v];
		DFS(v);
		top--;
	}
}
int main() {
//	freopen("P2305_4.in","r",stdin);
//	freopen("1.out","w",stdout);
	n=read(),m=read();
	
	memset(head,-1,sizeof(head));
	for(int i=2;i<=n;i++){
		fa[i]=read(),s[i]=read(),p[i]=read(),q[i]=read(),l[i]=read();
		add_Edge(fa[i],i);
	}
	
	dfs(1);
	top=tot=0;
	DFS(1);
	
	for(int i=2;i<=n;i++)
		printf("%lld\n",f[i]);
	return 0;
}
posted @ 2025-12-20 15:00  Harvey-zhuhy  阅读(1)  评论(0)    收藏  举报