CF1578L Labyrinth

Link

比较厉害的题。

简化问题,如果是不考虑吃能不能走的话,那很显然我们要求路径上最小边最大,这显然是重构树。

看能不能考虑建出重构树以后,往树上思考,能够发现,如果我每次吃了糖果,增加了宽度,那必然会造成一些边不能走,换言就是重构树上一些子树,我走不进去了。 这就把这个题转换成一个树上问题,我维护的是路径最小值最大,可以保证正确性。

如果一个子树 uu 的点权小于当前宽度,那说明这个子树里面的所有点就必须要被吃完。因为要尽可能不留下点没吃,这说明我们可以从限制最大的点开始考虑。(不然以后体型变大无法吃了,不符合题意。)

因为要求求初始值最大,最值嘛,考虑动归吧。考虑设 dpudp_u 表示走完以 uu 为根的子树,能有的初始值最大是多少。

每个点(非叶子)有且仅有两个儿子 x,yx,y。考虑转移。

发现对于两个儿子,完整走完一个,再去走另一个,相交走一部分 xx 去走 yy,其实是不劣的,因为你无论怎么走,吃完全部以后,都要回到当前点 uu,而这个点的点权是路径上的最小值,非常关键。(因为下面子树内的点权都会不小于它。)

这下就可以转移了啊,直接根据定义即可:

dpu=max(min(dpysx,cusx),min(dpxsy,cusy))dp_u=\max(\min(dp_y-s_x,c_u-s_x),\min(dp_x-s_y,c_u-s_y))

本质上就是分类讨论要先走 xx,还是先走 yy 的一个动态规划。

#include<bits/stdc++.h>
using namespace std;
const int N =4e5+10;
#define int long long 
struct node{
	int u,v,w;
}a[N];
int Ans[N],n,m,siz[N],v[N],F[N],c[N];
vector<int> g[N<<1];
int Find(int x){
	return x==F[x]?x:F[x]=Find(F[x]); 
}
bool cmp(node a,node b){
	return a.w>b.w;
}
void Kuskral_CG(){
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=2*n-1;i++)	F[i]=i; 
	int cnt=n;
	for(int i=1;i<=m;i++){
		int u=Find(a[i].u),v=Find(a[i].v);
		if(u==v)	continue;
		cnt++,F[u]=cnt,F[v]=cnt,g[cnt].push_back(u),g[cnt].push_back(v),c[cnt]=a[i].w;
	}
}
void dfs(int u,int fath){
	if(g[u].size()==0)	{siz[u]=v[u];return ;}
	if(c[g[u][0]]>c[g[u][1]])	dfs(g[u][1],u),dfs(g[u][0],u);
	else	dfs(g[u][0],u),dfs(g[u][1],u);
	siz[u]=siz[g[u][0]]+siz[g[u][1]]+v[u];
	Ans[u]=min(c[u]-siz[g[u][0]],Ans[g[u][1]]-siz[g[u][0]]);
	Ans[u]=max(Ans[u],min(c[u]-siz[g[u][1]],Ans[g[u][0]]-siz[g[u][1]]));
}
signed main(){
	memset(Ans,0x7f,sizeof(Ans));
	cin>>n>>m;	
	for(int i=1;i<=n;i++)	cin>>v[i];
	for(int i=1;i<=m;i++)	cin>>a[i].u>>a[i].v>>a[i].w;
	Kuskral_CG(),dfs(2*n-1,0);	
	cout<<((Ans[2*n-1]<=0)?-1:Ans[2*n-1]);
}
posted @   June_Failure  阅读(1)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示