[Codeforces GYM 101002K] YATP

XV.[Codeforces GYM 101002K] YATP

(没有单独的页面,就放个到大页面的连接罢)

我们考虑先套一个点分治。点分治后,考虑计算所有LCA为根节点的对中,最优的那些对。

我们考虑就算某两个点它们位于同一棵子树内也不要紧——这里它的权值被表示成 depi+depj+aiaj,但实际上再往子树内分治时该权值会更小,故实际上在此处计算也不会对最终答案有影响。故我们现在就要对于某个i,计算对于所有j(不管它是否与i在同一子树内)中,depi+depj+aiajmin

对于同一个i,显然depi是可以最后再考虑的。故我们只需要考虑aiaj+depj即可。它实际上是一个一次函数ajx+depj,而所有的j画到平面直角坐标系中就是一条条的直线。对于不同的x(即不同的ai),它可能需要不同的直线,故我们只需要求出上述直线的下凸壳即可。

O(n)扫一遍所有节点后,O(nlogn)排序并求凸壳。然后再扫一遍所有节点,在凸壳上二分出当前ai对应哪条直线最优即可。

时间复杂度O(nlog2n)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,a[200100],head[200100],cnt,sz[200100],msz[200100],rt,SZ,stk[200100],tp;
ll f[200100],res,dep[200100];
struct node{
	int to,next,val;
}edge[400100];
void ae(int u,int v,int w){
	edge[cnt].next=head[u],edge[cnt].val=w,edge[cnt].to=v,head[u]=cnt++;
	edge[cnt].next=head[v],edge[cnt].val=w,edge[cnt].to=u,head[v]=cnt++;
}
bool vis[200100];
void getroot(int x,int fa){
	sz[x]=1,msz[x]=0;
	for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa&&!vis[edge[i].to])getroot(edge[i].to,x),sz[x]+=sz[edge[i].to],msz[x]=max(msz[x],sz[edge[i].to]);
	msz[x]=max(msz[x],SZ-sz[x]);
	if(msz[x]<msz[rt])rt=x;
}
void getsz(int x,int fa){
	sz[x]=1;
	for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa&&!vis[edge[i].to])getsz(edge[i].to,x),sz[x]+=sz[edge[i].to];
}
vector<int>v;
void getdep(int x,int fa){
	v.push_back(x);
	for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa&&!vis[edge[i].to])dep[edge[i].to]=dep[x]+edge[i].val,getdep(edge[i].to,x);
}
int calc(int x){
	int l=1,r=tp;
	while(l<r){
		int mid=(l+r)>>1;
		if(1ll*a[stk[mid]]*a[x]+dep[stk[mid]]<=1ll*a[stk[mid+1]]*a[x]+dep[stk[mid+1]])r=mid;
		else l=mid+1;
	}
	f[x]=min(f[x],1ll*a[x]*a[stk[r]]+dep[stk[r]]+dep[x]);
}
void getroute(int x,int fa){
	calc(x);
	for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa&&!vis[edge[i].to])getroute(edge[i].to,x);
}
void getans(int x){
	dep[x]=0;
	getdep(x,0);
	sort(v.begin(),v.end(),[](int u,int v){return a[u]==a[v]?dep[u]>dep[v]:a[u]>a[v];});
	tp=0;
	for(auto i:v){
		while(tp>=2&&1ll*(dep[stk[tp-1]]-dep[stk[tp]])*(a[i]-a[stk[tp]])>=1ll*(dep[i]-dep[stk[tp]])*(a[stk[tp-1]]-a[stk[tp]]))tp--;
		stk[++tp]=i;
	}
	getroute(x,0);
	v.clear();
}
void solve(int x){
	getans(x),getsz(x,0),vis[x]=true;
	for(int i=head[x];i!=-1;i=edge[i].next)if(!vis[edge[i].to])rt=0,SZ=sz[edge[i].to],getroot(edge[i].to,x),solve(rt);
}
int main(){
	scanf("%d",&n),memset(head,-1,sizeof(head)),memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1,x,y,z;i<n;i++)scanf("%d%d%d",&x,&y,&z),ae(x,y,z);
	msz[0]=0x3f3f3f3f,rt=0,SZ=n,getroot(1,0),solve(rt);
	for(int i=1;i<=n;i++)res+=f[i];
	printf("%lld\n",res);
	return 0;
} 

posted @   Troverld  阅读(94)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示