CF1578L Labyrinth

kruskal重构树
一边并查集合并一边dp
然后从最小的边开始更新
f[i]表示吃完连通块i的最大初始值
合并u1,u2两个节点时
f[i]=max(min(w-c[u1],f[u1],f[u2]-S[u1]))
Kruskal重构树的性质
1.根据我们构造的过程,这是一个二叉堆(后面再讲构造)
2.原树两点之间的边权最大(小)值是重构树上两点Lca的权值
3.重构树中代表原树中的点的节点全是叶子节点,其余节点都代表了一条边的边权。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<cstring>
using namespace std;
#define inf 1000000000000000000
typedef long long ll;
#define maxn 400009
ll c[maxn],tot=0;;
int n,m;
int fa[maxn]; 
struct edge{
	int x,y;
	ll val;
}a[maxn];
int cmp(edge a,edge b)
{
	return a.val>b.val;
}
int Find(int x)
{
	if(fa[x]==x)return x;
	return fa[x]=Find(fa[x]);
}
bool ok=1;
ll f[maxn],s[maxn];
void Check(int x)
{
	if(x>n)return;
	f[x]=inf;
	s[x]=c[x];
}
ll Min(ll a,ll b,ll c){return min(a,min(b,c));}
void Merge(int i,int u1,int u2,ll w)
{
	//printf("Merge:%d %d
",u1,u2);
	Check(u1);Check(u2);
	s[i]=s[u1]+s[u2];
	f[i]=max(min(w-s[u1],f[u2]-s[u1]),min(f[u1]-s[u2],w-s[u2]));
//	if(f[i]<=0)ok=0;
}
signed main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=4*n;i++)fa[i]=i;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&c[i]);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].val);
		tot+=a[i].val;
	}
	sort(a+1,a+1+m,cmp);
	int ind=n;
	for(int i=1;i<=m;i++)
	{
		int x=a[i].x,y=a[i].y;
		ll val=a[i].val;
		if(Find(x)!=Find(y))
		{
			ind++;
			Merge(ind,Find(x),Find(y),val);
			fa[Find(x)]=fa[Find(y)]=ind;
		}
	}
//	for(int i=1;i<=ind;i++)
//	{
//		printf("f[%d]=%lld
",i,f[i]);
//		printf("s[%d]=%lld
",i,s[i]);
//	}
	if(f[ind]<=0)printf("-1
");
	else printf("%lld
",f[ind]);
	return 0;
}
原文地址:https://www.cnblogs.com/lzy-blog/p/15367267.html