ABC383E 題解
題意
給定一張包含 \(n\) 個節點和 \(m\) 條無向帶權邊的圖,以及兩個序列 \(A_k,B_k\) 分別表示圖中的某些節點,定義 \(f(A_i,B_j)\) 為從 \(A_i\) 到 \(B_j\) 所有路徑各自包含的邊權最大值中的最小值,可以任意排列 \(B\) 中的元素,使得 \(\sum_{i=1}^kf(A_i,B_i)\) 最小化,求出這個最小值。
分析
結論
轉化為最小生成樹的問題,貪心,在 Kruskal 的過程中額外記錄一下每個點集中待匹配的 \(A,B\) 元素個數,每次連線兩個不連通點集 \(V_1,V_2\) 的時候,儘可能多地把 \(V_1\) 中的 \(A\) 和 \(V_2\) 中的 \(B\) 匹配,把 \(V_1\) 中的 \(B\) 和 \(V_2\) 中的 \(A\) 匹配,代價為:當前邊的邊權 乘以 匹配數。
證明1
首先,需要明確為什麼“當前邊的邊權”是路徑中的“邊權最大值”。
根據 Kruskal 演算法的原理,我們先對邊權進行了排序,那麼我們一定是按照邊權大小升序排序來列舉邊。
假設在列舉過程中,當前列舉到了一條權值為 \(w\),可以連線 \(u\) 和 \(v\) 兩個未連通點的邊,那麼我們會選擇這條邊,並把 \(u,v\) 所在集合連線起來,所以 \(u\) 到 \(v\) 路徑中一定包含了這條邊(因為在最後的最小生成樹中,任意兩節點之間的路徑唯一),並且這條邊的邊權是當前考慮過的所有邊中最大的,顯然它也是這條路徑上最大的。
在之後對邊的列舉中,任何選取都不會對 \(u,v\) 路徑產生影響了。
證明2
然而這只是一條路徑的邊權最大值,那麼它為什麼是 \(u\) 到 \(v\) 所有路徑的邊權最大值中的“最小值”呢?
採用反證法,記我們在最小生成樹的流程中選出來的這條邊的邊權為 \(MX1\) ,假設存在另一條以 \(MX2\) 為邊權最大值的路徑,使得 \(MX2<XM1\) ,那麼畫個圖思考一下會發現,我們根本就不會選擇 \(MX1\) 這條邊作為最小生成樹的一部分,矛盾,所以假設不成立。
證明3
至於為什麼要"儘可能多地匹配",這一點從貪心地角度想應該是很明瞭的,由於 \(B\) 內部是可以取任意順序的,又因為我們列舉邊的順序是從小到大,無論如何都要做滿 \(k\) 次匹配,那肯定是讓每一次匹配儘量產生較小的答案,最後的答案一定是最小的。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,k;
const int N=2e5+10;
struct Edge
{
int u,v,w;
const bool operator <(Edge tmp)const{return w<tmp.w;}
}e[N];
int a[N],b[N];
int fa[N];
int siza[N],sizb[N];
inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>k;
for(int i=1,u,v,w;i<=m;++i)
{
cin>>u>>v>>w;
e[i]={u,v,w};
}
for(int i=1;i<=k;++i)cin>>a[i],siza[a[i]]++;
for(int i=1;i<=k;++i)cin>>b[i],sizb[b[i]]++;
for(int i=1;i<=n;++i)fa[i]=i;
sort(e+1,e+m+1);
int ans=0;
for(int i=1;i<=m;++i)
{
int fx=find(e[i].u),fy=find(e[i].v);
if(fx==fy)continue;
int w=e[i].w;
int mn=min(siza[fx],sizb[fy]);
ans+=w*mn,siza[fx]-=mn,sizb[fy]-=mn;
mn=min(sizb[fx],siza[fy]);
ans+=w*mn,sizb[fx]-=mn,siza[fy]-=mn;
fa[fx]=fy;
siza[fy]+=siza[fx],sizb[fy]+=sizb[fx];
siza[fx]=0,sizb[fx]=0;
//merge
}
cout<<ans;
return 0;
}