[多校 NOIP 联合模拟 11.30 T4] ZZH 的旅行----线段树合并+李超树

题面

题解

dp_i表示从i开始旅行的最大有趣度,从上面的简化题意中可知,该值只与相邻两处景点有关,于是有:

d_i为从1到i的路径长度,把dis(u,v)化成d_v-d_u,可以得出

观察一下发现,每个点v可以看作一条直线y=b_vx+dp_v-b_vd_v,然后查询所有直线在x=a_u+d_uy的最大值。

这个问题非常熟悉,可以用李超树解决。怎么把所有儿子的李超树的信息合并起来呢?也非常熟悉,用线段树合并即可。

(为了写这篇题解,我还专门写了两者的博客😥)

由于每个儿子的直线数加起来不超过n,线段树合并总复杂度不超过O(nlogn),由于李超树插入的是直线,每次复杂度O(logn)

总复杂度O(nlogn)

代码

这绝对是这题最短的代码之一(80行)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#define ll long long
#define MAXN 1000005
#define INF 0x7f7f7f7f
#define R 2000000000ll
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,IN,root[MAXN];
ll a[MAXN],b[MAXN],d[MAXN],ans[MAXN];
struct itn{
	int v;ll w;
	itn(){}
	itn(int V,ll W){v=V,w=W;}
};
vector<itn>G[MAXN];
struct lcs{  //动态开点李超树
	int ls,rs;ll k,b;lcs(){}
	lcs(ll K,ll B){ls=rs=0,k=K,b=B;}
}t[MAXN<<4];
inline void add(int x,ll l,ll r,ll k,ll b){
	if(x==0)return;
	ll tk=t[x].k,tb=t[x].b,mid=(l+r)>>1;
	if(l*k+b>=l*tk+tb&&r*k+b>=r*tk+tb){t[x].k=k,t[x].b=b;return;}
	else if(l*k+b<l*tk+tb&&r*k+b<r*tk+tb)return;
	else{
		if(!t[x].ls)t[x].ls=++IN,t[IN]=lcs(tk,tb);
		else add(t[x].ls,l,mid,tk,tb);
		if(!t[x].rs)t[x].rs=++IN,t[IN]=lcs(tk,tb);
		else add(t[x].rs,mid+1,r,tk,tb);
		t[x].k=k,t[x].b=b;
	}
}
inline int mergg(int x,int y,ll l,ll r){
	if(!x||!y)return x|y;
	ll mid=(l+r)>>1;
	t[x].ls=mergg(t[x].ls,t[y].ls,l,mid),t[y].ls=0;
	t[x].rs=mergg(t[x].rs,t[y].rs,mid+1,r),t[y].rs=0;
	add(x,l,r,t[y].k,t[y].b);
	return x;
}
inline ll sch(int x,ll l,ll r,ll g){
	if(x==0)return -INF;
	ll mid=(l+r)>>1,res=g*t[x].k+t[x].b;
	if(g<=mid)res=max(res,sch(t[x].ls,l,mid,g));
	else res=max(res,sch(t[x].rs,mid+1,r,g));
	return res;
}
inline void dfs(int x,int fa,ll dx){
	d[x]=dx,root[x]=++IN,t[IN]=lcs(0,-INF);
	for(int i=0;i<G[x].size();i++)
		if(G[x][i].v!=fa){
			int v=G[x][i].v;ll w=G[x][i].w;
			dfs(v,x,dx+w),root[x]=mergg(root[x],root[v],0,R);
		}
	ans[x]=max(0ll,sch(root[x],0,R,a[x]+d[x]));
	add(root[x],0,R,b[x],ans[x]-b[x]*d[x]);
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();ll w=read();
		G[u].push_back(itn(v,w)),G[v].push_back(itn(u,w));
	}
	dfs(1,0,0);
	for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值