CF76A
最小生成树妙题。
观察一下数据范围,,。大概是 或者多个 的。
从暴力谈起,先按 升序排序。然后枚举 作为购买金币数量。然后将 跑 ,相当于每次加一条边。银币数量其实也就是最小生成树中最大的边权。
但是这样复杂度是 的,想想该如何优化。了解下最小生成树的性质:只有 条边,是树。
此时找性质:有些边由于不优被放弃后,就不会成为树边了。因为最小生成树随着加边只会更优,如果之前都不在里面了,那么之后如果要用它,为什么不用原先更优秀的那个呢?
然后就可以把边的数量降至 。此时复杂度 。此时瓶颈在于排序,发现可以因为边集合有序,插入一个的复杂度其实只需要 。复杂度变成 。稳过。
#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;
}