树上分块解决限制距离的树上 DP 问题([NOI2014] 购票)

[NOI2014] 购票

大家好,我喜欢暴力数据结构,所以我用分块过了此题。


转移方程很简单:

\[f_u=\min_{d_u-d_v\leq l_u}{(d_u-d_v)\times p_u+q_u+f_v} \]

\[f_u=d_u\times p_u+q_u\min_{d_v\geq l_u-d_u}{-d_v\times p_u+f_v} \]

后面那部分一条 \(y=kx+b\) 的直线在 \(x=p_u\) 处的取值,很容易想到用李超线段树维护了。

如果没有距离限制的话,就可以直接用可持久化李超线段树解决。(或者可撤销李超树?)

加上距离的限制的话,想到 P4192 旅行规划 中分块维护凸包的做法,于是对于 \(t=0\)\(2\) 的数据点就解决了。

转化到树上,也可以分块解决。

对深度进行分块。设块长为 \(B\) 深度 \(\left[kB,(k+1)B\right)\) 的分为一层,每一层内部进行可持久化上下继承信息。不同层之间互不影响。

实际上是借助可持久化数据结构不受兄弟影响的性质把树夹扁成一条链了。

询问使如果该节点到这一层顶端都在距离限制以内,就直接在李超树上查询,然后跳到上面一层。

容易维护出 \(pre_i\) 表示 \(i\) 号节点的祖先(包括自己)中最下面一个深度为 \(B\) 的整数倍的节点。

然后散块暴力转移就好。

块长 \(B\)\(\sqrt{n\log n}\) 时,复杂度为 \(n\sqrt{n\log n}\)

实际上跑得挺快的,特判了 \(t=0\) 或 1 的数据点后在最优解第二页,而且代码很短!五十行,1.74K 是最优解前四页里面最短的了。

#include<bits/stdc++.h>
#define EL puts("Elaina")
#define reg register int
typedef long long ll;
using namespace std;
namespace IO{
	const int siz=1<<18;char buf[siz],*p1,*p2;
	inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,siz,stdin),p1==p2)?EOF:*p1++;}
	template<typename T> inline T read(){
		T x=0;char ch=getc();
		while(!isdigit(ch))ch=getc();
		while(isdigit(ch))x=x*10+(ch^48),ch=getc();
		return x;
	}
}using IO::read;
const int maxn=2e5+3;const ll INF=0x3f3f3f3f3f3f3f3f;
struct line{ll k,b;inline ll operator ()(const int &x)const{return k*x+b;}};
int tot;
#define mid (l+r>>1)
struct SegmentTree{
	struct{int lt,rt;line val;}t[maxn*26];
	inline void insert(int &p,int q,int l,int r,line val){
		t[p=++tot]=t[q];
		if(val(mid)<t[p].val(mid))swap(val,t[p].val);
		if(l==r)return;
		if(val.k>t[p].val.k)insert(t[p].lt,t[q].lt,l,mid,val);
		else insert(t[p].rt,t[q].rt,mid+1,r,val);
	}
	inline void query(int k,int l,int r,int x,ll &ans){
		ans=min(ans,t[k].val(x));if(!k||l==r)return;
		x<=mid?query(t[k].lt,l,mid,x,ans):query(t[k].rt,mid+1,r,x,ans);
	}
}T;
#undef mid
int n,block,fa[maxn],pre[maxn],dep[maxn],root[maxn];ll d[maxn],f[maxn];
inline void MyDearMoments(){
	n=read<int>(),T.t[0].val.b=INF,block=read<int>()<2?n+1:sqrt(n*log(n));
	T.insert(root[1],0,0,1000000,{0,0});
	for(reg i=2;i<=n;++i){
		int u=fa[i]=read<int>();d[i]=read<ll>()+d[fa[i]];
		int p=read<int>();ll q=read<ll>(),lim=d[i]-read<ll>();
		dep[i]=dep[u]+1,pre[i]=dep[i]%block==0?i:pre[u],f[i]=LLONG_MAX;
		while(u&&d[pre[u]]>=lim)T.query(root[u],0,1000000,p,f[i]),u=fa[pre[u]];
		while(u&&d[u]>=lim)f[i]=min(f[i],-d[u]*p+f[u]),u=fa[u];
		printf("%lld\n",f[i]+=d[i]*p+q);
		T.insert(root[i],dep[i]%block==0?0:root[fa[i]],0,1000000,{-d[i],f[i]});
	}
}
int main(){return MyDearMoments(),0;}
posted @ 2023-01-12 20:17  Muel_imj  阅读(153)  评论(1编辑  收藏  举报