CF2081D - MST in Modulo Graph

D. MST in Modulo Graph

听说是一类题型,所以且做记录。

引用官方题解:

注意到,有用边的数量不超过 \(n\log{n}\)

我哪来的注意力

通过枚举不同 \(p_i\) 的权值 \(k\),使得其他数值在 \((kp_i, \, (k+1)p_i)\) 的范围内,容易发现,满足 \(kx \le y < z < (k+1)x\) 的点,连接 \((x, \, y), \, (y, \, z)\) 显然比 \((x, \, y), \, (z, \, x)\) 更优秀,所以通过这样枚举后二分出第一个满足条件的 \(y\),复杂度是调和级数,单调栈可以做到 \(O(n\log{n})\),处理边(基数排序可以做到 \(O(n\log{n})\))后 Kruskal 算法上一波就行了。

复杂度 \(O(n\log^2{n})\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PII;
const int N = 5e5 + 10;
int p[N];

int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void solve() {
    int n;
    cin >> n;
    vector<int> a;
    a.push_back(0);
    for (int i = 0; i < n; i ++ ) {
        int t;
        cin >> t;
        a.push_back(t);
    }
    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
    n = a.size() - 1;
    vector<PII> edge, w;
    int maxn = *max_element(a.begin(), a.end());
    for (int i = 1; i < n; i ++ ) {
        for (int k = 1; k * a[i] <= maxn; k ++ ) {
            int t = lower_bound(a.begin() + i + 1, a.end(), k * a[i]) - a.begin();
            if (a[t] >= (k + 1) * a[i] || a[t] < k * a[i]) continue;
            // cout << a[t] << " <-> " << a[i] << " k = " << k << " " << a[t] - k * a[i] << "\n";
            w.push_back({a[t] - k * a[i], edge.size()});
            edge.push_back({i, t});
        }
    }
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    sort(w.begin(), w.end());
    int ans = 0;
    for (auto items : w) {
        int x = edge[items.second].first, y = edge[items.second].second;
        x = find(x), y = find(y);
        if (x != y) {
            p[x] = y;
            ans += items.first;
        }
    }
    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T -- ) solve();
    return 0;
}
posted @ 2025-03-17 19:35  YipChip  阅读(21)  评论(0)    收藏  举报