*题解:P14415 [JOISC 2015] 遗产继承 / Inheritance
解析
对于第一个人,其所选的边构成的图即为原图中的最大生成森林,第二个人在剩下的边组成的图中选取最大生成森林,以此类推。这样暴力地跑 \(K\) 遍 Kruskal 的时间复杂度为 \(O(KM\log M)\),无法通过。发现每次都遍历所有边浪费了很多时间,所以考虑把目前还未被选取的边用一个 set 维护,每次选中某条边就将其删掉。然而,这样我们还是有可能会遍历很多没有被选且选不了的边。
考虑只遍历一次边,对于一条边 \((u,v)\),如果第一个人不能选,就尝试将其给第二个人,以此类推,相当于将之前的 \(K\) 轮选取合到一轮做了。直接做的话复杂度仍然不对,但由于我们是要按顺序找第一个 \(u,v\) 不连通的图,且根据我们加边的策略,可以保证对于点对 \(u,v\),其在前面若干个图中连通,在后面所有图中不连通,故使用二分解决。
时间复杂度 \(O(M\log M + M\log N\log K)\),当然这是并查集只写按秩合并或只写路径压缩时的结果。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000 + 5,M = 300000 + 5,K = 10000 + 5,mod = 998244353;
struct Edge{
int u,v,w,id;
friend bool operator > (Edge a,Edge b){
return a.w > b.w;
}
};
vector<Edge> e;
int fa[K][N],res[M];
int find(int *f,int x){
if(x != f[x]) f[x] = find(f,f[x]);
return f[x];
}
void merge(int *f,int a,int b){
int p = find(f,a),q = find(f,b);
f[p] = q;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out1.txt","w",stdout);
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++){
fa[i][j] = j;
}
}
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
e.push_back({a,b,c,i});
}
sort(e.begin(),e.end(),greater<Edge>());
for(int i=0;i<m;i++){
int l = 1,r = k + 1;
int u = e[i].u,v = e[i].v,id = e[i].id;
while(l < r){
int mid = (l + r) / 2;
if(find(fa[mid],u) == find(fa[mid],v)){
l = mid + 1;
}else{
r = mid;
}
}
if(l <= k){
merge(fa[l],u,v);
res[id] = l;
}
}
for(int i=1;i<=m;i++){
cout<<res[i]<<'\n';
}
return 0;
}

浙公网安备 33010602011771号