*题解: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;
}
posted @ 2026-08-21 09:54  yutar  阅读(4)  评论(0)    收藏  举报