Codeforces 2081D MST in Modulo Graph / Luogu P7789 [COCI 2016/2017 #6] Sirni 题解 [ 蓝 ] [ 最小生成树 ] [ MST 优化建图 ] [ 调和级数 ]

MST in Modulo Graph:MST 好题。

一般这种完全图求 MST 的题,有两种思路:

  • 优化建图,根据图的性质砍掉一些无用边。
  • 使用 Boruvka 算法,搭配某些数据结构求每个节点的最小边。

本题中取模的条件用数据结构比较难维护,于是考虑优化建边的思路。

首先,值相同的点显然是可以先缩掉的,因为它们之间的边权值为 \(0\)

接下来对每一个节点 \(a_i\) 考虑它作为模数时的边,然后枚举模数的所有倍数 \(ka_i\),发现当两个数 \(x,y\) 满足 \(ka_i < x < y \le (k+1)a_i\) 的时候,连 \((a_i,x)\) 一定比 \((a_i,y)\) 更优。因为 \((x,y)\) 的权值是 \(y - x\),而 \((a_i,y)\) 的权值为 \(y - ka_i\),显然更大。于是我们可以连接 \((a_i, x), (x, y)\),比 \((a_i, x), (a_i, y)\) 更优。

建图后跑任意一个最小生成树算法即可。因为枚举每个数的倍数是调和级数复杂度,所以时间复杂度是跑不满的 \(O(V\log^2 V)\)

对于 CF2081D 中,此题 \(n, V\) 同阶,不用考虑一些复杂度的问题。而对于 P7789,此时 \(n,V\) 不同阶,复杂度就不是严格的 \(O(n\log^2 n)\) 了。严格的计算比较复杂,这里我们可以分段计算,大概估算一下边的上界。具体地,分段计算 \(10^i\sim 10^{i + 1}\),这段区间内的边数上界是 \(10^{i + 1}\min(n, \dfrac{V}{10^{i + 1}})\)。将 \(i = 0\sim 6\) 带入,得到一个宽松的边数上界:\(6.1\times 10^7\)。空间和时间都比较卡,但是足以通过了。

CF2081D:

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 500005, M = 20000005;
int n, m, a[N];
struct Edge{
    int u, v;
    ll w;
    bool operator < (const Edge & t) const{
        return (w < t.w);
    }
}e[M];
int getnxt(int x)
{
    return (lower_bound(a + 1, a + n + 1, x) - a);
}
int fa[N];
void init()
{
    for(int i = 1; i <= n; i++) fa[i] = i;
}
int findf(int x)
{
    if(fa[x] != x) fa[x] = findf(fa[x]);
    return fa[x];
}
void combine(int x, int y)
{
    int fx = findf(x), fy = findf(y);
    fa[fx] = fy;
}
void solve()
{
    cin >> n;
    m = 0;
    for(int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    n = unique(a + 1, a + n + 1) - a - 1;
    for(int i = 1; i <= n; i++)
    {
        for(int j = a[i]; j <= a[n]; j += a[i])
        {
            int v = getnxt(j);
            if(j == a[i]) v = getnxt(j + 1);
            if(v > n || j / a[i] != a[v] / a[i]) continue;
            e[++m] = {i, v, a[v] % a[i]};
        }
    }
    sort(e + 1, e + m + 1);
    init();
    ll ans = 0;
    for(int i = 1; i <= m; i++)
    {
        int u = e[i].u, v = e[i].v; ll w = e[i].w;
        if(findf(u) != findf(v))
        {
            combine(u, v);
            ans += w;
        }
    }
    cout << ans << "\n";
}
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}

P7789:

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 500005, M = 61000005;
int n, m, a[N];
struct Edge{
    int u, v, w;
    bool operator < (const Edge & t) const{
        return (w < t.w);
    }
}e[M];
int getnxt(int x)
{
    return (lower_bound(a + 1, a + n + 1, x) - a);
}
int fa[N];
void init()
{
    for(int i = 1; i <= n; i++) fa[i] = i;
}
int findf(int x)
{
    if(fa[x] != x) fa[x] = findf(fa[x]);
    return fa[x];
}
void combine(int x, int y)
{
    int fx = findf(x), fy = findf(y);
    fa[fx] = fy;
}
void solve()
{
    cin >> n;
    m = 0;
    for(int i = 1; i <= n; i++) cin >> a[i];
    sort(a + 1, a + n + 1);
    n = unique(a + 1, a + n + 1) - a - 1;
    for(int i = 1; i <= n; i++)
    {
        for(int j = a[i]; j <= a[n]; j += a[i])
        {
            int v = getnxt(j);
            if(j == a[i]) v = getnxt(j + 1);
            if(v > n || j / a[i] != a[v] / a[i]) continue;
            e[++m] = {i, v, a[v] % a[i]};
        }
    }
    sort(e + 1, e + m + 1);
    init();
    ll ans = 0;
    for(int i = 1; i <= m; i++)
    {
        int u = e[i].u, v = e[i].v; ll w = e[i].w;
        if(findf(u) != findf(v))
        {
            combine(u, v);
            ans += w;
        }
    }
    cout << ans << "\n";
}
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t = 1;
    while(t--) solve();
    return 0;
}
posted @ 2025-08-31 02:33  KS_Fszha  阅读(14)  评论(0)    收藏  举报