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;
}