題面
題意
有n個點,並給出k條還沒有權值的邊和m條有權值的邊,要求你給這k條邊賦上權值,使得存在一種最小生成樹包含這k條邊,問k條邊的最大權 值和是多少。
做法
首先可以先求出這棵最小生成樹,然後這m條邊中的所有非樹邊都會與原樹構成一個環,要滿足它不是樹邊,它就必須是環上的最大值,相當於要對環上的所有樹邊都取一次最小值,這個用樹鏈剖分,差分+啓發式合併或可並堆…都可以做,但都很麻煩。
可以發現,如果將所有操作的值排序(輸入其實已經排好序了),這樣每次操作賦的值是遞增的,也就是說一條邊一旦被賦值就不會再被修改了,爲了防止重複賦值,一旦一條邊的權值已經確定,就將它連接的父子用並查集合並,這樣每條邊只會被掃到一次,總時間複雜度僅爲
代碼
#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;
}