题面
题意
有n个点,并给出k条还没有权值的边和m条有权值的边,要求你给这k条边赋上权值,使得存在一种最小生成树包含这k条边,问k条边的最大权 值和是多少。
做法
首先可以先求出这棵最小生成树,然后这m条边中的所有非树边都会与原树构成一个环,要满足它不是树边,它就必须是环上的最大值,相当于要对环上的所有树边都取一次最小值,这个用树链剖分,差分+启发式合并或可并堆…都可以做,但都很麻烦。
可以发现,如果将所有操作的值排序(输入其实已经排好序了),这样每次操作赋的值是递增的,也就是说一条边一旦被赋值就不会再被修改了,为了防止重复赋值,一旦一条边的权值已经确定,就将它连接的父子用并查集合并,这样每条边只会被扫到一次,总时间复杂度仅为
O
(
n
)
O(n)
O(n)
代码
#include<bits/stdc++.h>
#define ll long long
#define N 500100
using namespace std;
ll n,K,m,sum,bcj[N],ans[N],a[N],b[N],c[N],deep[N],fa[N];
bool tree[N],need[N];
vector<ll>to[N],tr[N];
ll ff(ll u){return u==bcj[u]?u:bcj[u]=ff(bcj[u]);}
void dfs(ll now,ll last)
{
ll i,t;
for(i=0;i<to[now].size();i++)
{
t=to[now][i];
if(t==last) continue;
deep[t]=deep[now]+1;
need[t]=tr[now][i];
fa[t]=now;
dfs(t,now);
}
}
int main()
{
ll i,j,p,q;
cin>>n>>K>>m;
for(i=1;i<=n;i++) bcj[i]=i;
for(i=1;i<=K;i++)
{
scanf("%lld%lld",&p,&q);
bcj[ff(p)]=ff(q);
to[p].push_back(q),to[q].push_back(p);
tr[p].push_back(1),tr[q].push_back(1);
}
for(i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&p,&q,&c[i]);
a[i]=p,b[i]=q;
if(ff(p)==ff(q)) continue;
tree[i]=1;
bcj[ff(p)]=ff(q);
to[p].push_back(q),to[q].push_back(p);
tr[p].push_back(0),tr[q].push_back(0);
}
deep[1]=1;
dfs(1,-1);
for(i=1;i<=n;i++) bcj[i]=i;
for(i=1;i<=m;i++)
{
if(tree[i]) continue;
p=ff(a[i]),q=ff(b[i]);
for(;p!=q;)
{
if(deep[p]<deep[q]) swap(p,q);
ans[p]=c[i];
bcj[p]=ff(fa[p]);
p=ff(p);
}
}
for(i=1;i<=n;i++)
{
if(!need[i]) continue;
if(!ans[i])
{
puts("-1");
return 0;
}
sum+=ans[i];
}
cout<<sum;
}