嘘~ 正在从服务器偷取页面 . . .

ABC210E Ring MST 题解


ABC210E Ring MST 题解

题目链接:ABC210E Ring MST

题意:给定一张 \(n\) 个点的无向图,顶点的编号为 \(0,1,\dots,n−1\) 。同时给出两个长度为 \(m\) 的数组 \(a_1,a_2,\dots,a_m\)\(b_1,b_2,\dots,b_m\) 。初始时图中并没有任何边,你可以按照以下操作加边:

选择一个 \(1\le i\le m\) 和一个 \(0<x<n\) ,并在顶点 \(x\) 和顶点 \((x+a_i) \bmod n\) 中添加一条长度为 \(b_i\) 的边。

你现在想要知道,你添加的边的长度总和至少为多少,才能使得整个图连通?如果无论如何都不能使整个图连通,输出 \(−1\)

数据范围:

\(1 \le a_i < n \le 10^9,~1 \le m \le 10^5,1\le b_i \le 10^9\)

首先我们可以想到把 \((x,(x+a_i)\bmod n)\) 看作一条边

然后答案就是最小生成树,跑个 \(\text{kruskal}\)

不过这个是 \(O(nm)\) 的,这是不能接受的

于是我们模拟 \(\text{kruskal}\) 的算法过程,不过更加快捷

\((a_i,b_i)\)\(b_i\) 排序升序排序,保证有 \(\forall i < j,~b_i\le b_j\)

然后考虑尽可能的用较前的 \((a_i,b_i)\) 来建边

有一个结论:令 \(g=\gcd(a_i,n)\)

所有模 \(g\) 相同的结点均可以连到同一个连通块,并且会有 \(g\) 个这样的连通块

注:此时我们尽可能在用 \(a_i\) 建边,这里连到同一个连通块就是指尽可能建边的结果

这个结论其实证起来很简单

方便起见,我们固定 \(0\) 为起点,显然这不会影响答案

则只用 \(a_i\)\(0\) 可以走到的点一定是 \[ (a_ix+ny) \bmod n \] 其中 \(x,y \in \mathbb{Z}\)

\(c=a_ix+ny\) ,根据裴蜀定理可知,如果有解,则 \(g \mid c\)

注意我们这里是 \(c\) ,实际上我们还会从 \(0,1,\dots,g-1\) 出发去连边

不难发现这样我们会连出 \(g\) 个这样的连通块

然后我们试着去推广这个结论

如果我们用前 \(i\)\(a_i\) ,则他们一共有 \(g=\gcd(a_1,a_2,\dots,a_i,n)\) 个连通块

详细证明我也不太会,但是这个就是个很显然的结论

不难发现,当 \(g=1\) 时,图就联通了。

然后这题就没了。边权只要用 \(\sum(g_{i-1}-g_{i}) \times b_{i}\) 来算就行啦

这题我在Roundgod老师的指导下想了一个小时左右才想明白 QAQ

时间复杂度 \(O(m\log m)\)

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define F first
#define S second
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)()
typedef pair<int,int> P;
int n,m;
vector<P> vec;
int gcd(int a,int b)
{return b==0?a:gcd(b,a%b);}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m; vec.clear();
    for(int i=1,u,v; i<=m; i++)
    {
        cin >> u >> v;
        vec.push_back({v,u});
    }
    sort(vec.begin(),vec.end());
    int ans=0,g=n;
    for(int i=0; i<m; i++)
    {
        int tmp=g;
        g=gcd(g,vec[i].S);
        ans+=(tmp-g)*vec[i].F;
    }
    if(g!=1) cout << "-1\n";
    else cout << ans << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录