CF76A

最小生成树妙题。

观察一下数据范围,n200n\leq 200m2×104m\leq 2\times 10^4。大概是 O(nm)O(nm) 或者多个 log\log 的。

从暴力谈起,先按 gg 升序排序。然后枚举 gig_i 作为购买金币数量。然后将 s1sis_1\sim s_iKruskalKruskal,相当于每次加一条边。银币数量其实也就是最小生成树中最大的边权。

但是这样复杂度是 O(m2logm)O(m^2\log m) 的,想想该如何优化。了解下最小生成树的性质:只有 n1n-1 条边,是树。

此时找性质:有些边由于不优被放弃后,就不会成为树边了。因为最小生成树随着加边只会更优,如果之前都不在里面了,那么之后如果要用它,为什么不用原先更优秀的那个呢?

然后就可以把边的数量降至 O(n)O(n)。此时复杂度 O(mnlogn)O(mn\log n)。此时瓶颈在于排序,发现可以因为边集合有序,插入一个的复杂度其实只需要 O(n)O(n)。复杂度变成 O(mn)O(mn)。稳过。

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 5e5+10;
int n,m,G,S,c;
struct Nd{
	int x,y,g,s;
}a[N],b[N];
int f[N];
int fi(int x) {
	return f[x] == x ? f[x] : f[x] = fi(f[x]); 
}
int cnt;
int ans = LONG_LONG_MAX;
void klska(int i) {
//	});
	long long p = 0; cnt = 0;
	for(int i=1;i<=n;i++) f[i] = i;
	for(int i=1;i<=c;i++) {
		int fx = fi(a[i].x), fy = fi(a[i].y);
		if(fx == fy) continue;
		f[fx] = fy; p=max(p,a[i].s); a[++cnt] = a[i];
	}
	c = cnt;
	if(cnt == n-1) {
		ans = min(ans,p*S+b[i].g*G);
	}
}
signed main() {
	cin>>n>>m>>G>>S;
	for(int i=1;i<=m;i++) {
		cin>>b[i].x>>b[i].y>>b[i].g>>b[i].s;
	}
	sort(b+1,b+m+1,[](Nd x,Nd y) {
		return x.g<y.g;
	});
	for(int i=1;i<=m;i++) {
		int p = c+1;
		while(p-1>0 && b[i].s<a[p-1].s) {
			a[p]=a[p-1];
			p--;
		}
		c++;
		a[p] = b[i];
		klska(i);
	}
	if(ans>4e18) cout<<-1;
	else
	cout<<ans;
	return 0;
}
posted @ 2024-02-02 20:19  cjrqwq  阅读(7)  评论(0)    收藏  举报  来源