gym 102331 F. Fast Spanning Tree
https://codeforces.com/gym/102331/problem/F
学到许多
首先有个显然的性质,假设一条边两边的权值和分别是\(x,y\),这条边的要求是\(S\)
由\(x+y \ge S\) 可以得到
\(x \ge \frac{S}{2} \ \ or \ \ y \ge \frac{S}{2}\)
我们把这条边的限制(上界)平均分别丢到\(x, y\)上,拿一个小根堆维护
\(x+\frac{s-x-y}{2}\)
当启发式合并两个联通块\(u,v(siz[u]<siz[v])\)时
\(a[v]+=a[u]\),把\(u\)的小根堆合并进去
然后查看堆顶,如果\(a[v]\ge\) 堆顶的限制,那么说明\(v\)已经达到堆顶那条边的限制之一了,假设堆顶那条边是\((x,y,s)\), 那么只需要判断如果\(a[x]+a[y]\ge s\)就加入答案,否则把剩下的\(s-a[x]-a[y]\)再平均分配到\(x,y\)两个点上面的堆里,作为新的限制。
即新的上界限制是\(a[x]+\frac{s-a[x]-a[y]}{2}\),\(y\)同理
启发式合并的时间复杂度是\(O(nlog^2n)\)的,然后发现每条边最多被访问\(logC\)次
容易发现这样均摊下来时间复杂度是\(O(nlog^2n+nlogC)\)的
完全能跑
可以结合代码理解
code:
#include<bits/stdc++.h>
#define N 400050
#define fi first
#define se second
using namespace std;
const int inf = 1e9;
struct E {
int u, v, c;
} e[N << 1];
int fa[N], a[N], ans[N], gs;
priority_queue<pair<int, int> > q[N];
priority_queue<int> ok;
int get(int x) {
return x == fa[x]? x : (fa[x] = get(fa[x]));
}
void add(int i) { //printf("* %d\n", i);
int x = get(e[i].u), y = get(e[i].v);
if(x == y) return ;
if(a[x] + a[y] >= e[i].c) {
ok.push(- i);
return ;
}
int o = (e[i].c - a[x] - a[y] + 1) / 2;
q[x].push(make_pair(-(a[x] + o), i));
q[y].push(make_pair(-(a[y] + o), i));
}
void merge(int u, int v, int i) {
// printf("%d %d %d\n", u, v, i);
u = get(u), v = get(v);
if(u == v) return ;
ans[++ gs] = i;
if(q[u].size() > q[v].size()) swap(u, v);
fa[u] = v; a[v] = a[u] + a[v];
if(a[v] > inf) a[v] = inf;
while(q[u].size()) {
auto x = q[u].top(); q[u].pop();
if(-x.fi <= a[v]) add(x.se);
else q[v].push(x);
}
while(q[v].size()) {
auto x = q[v].top(); q[v].pop();
// printf("%d %d\n", x.fi, x.se);
if(-x.fi <= a[v]) add(x.se);
else {
q[v].push(x);
break;
}
}
}
int n, m;
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 1; i <= m; i ++) {
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].c);
add(i);
}
while(ok.size()) {
int i = -ok.top(); ok.pop();
merge(e[i].u, e[i].v, i);
}
//sort(ans + 1, ans + 1 + gs);
printf("%d\n", gs);
for(int i = 1; i <= gs; i ++) printf("%d ", ans[i]);
return 0;
}