ABC 210 E - Ring MST
E - Ring MST
数论,最小生成树
-
根据 kruskal 的原理,将边排序取最小的、可以两端点未联通的前 \(n-1\) 条边即可,但本题的边数是 \(n*m\) 级别,不能排序
-
只有 \(m\) 种操作,每种操作的代价是相同的,第 \(i\) 种操作是连接 \(x\) 与 \((x+A_i)\mod n\) ,根据数论的知识,设 \(d=\gcd (n,A_i)\), 则这种操作最多将
\(0,d,2d,3d...\)
\(1,d+1,2d+1...\)
\(2,d+2,2d+2...\)
...
这些点联通,留下 \(0,1,2,...,d-1\) 这些点为代表的连通块
-
所以可将这 \(m\) 个操作的代价从小到大排序,每种操作可以将 \(n\) 个连通块变为 \(d\) 个连通块,代价为 \((n-d)*c\)
当 \(n=1\) 时则图已联通
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
#define endl "\n"
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int n, m;
int gcd(int a, int b)
{
if (b == 0)
return a;
return gcd(b, a % b);
}
struct Node
{
int a;
ll c;
bool operator<(const Node &x) const
{
return c < x.c;
}
}op[N];
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> op[i].a >> op[i].c;
sort(op + 1, op + m + 1);
ll ans = 0;
int cnt = 0;
for (int i = 1; i <= m; i++)
{
auto [a, c] = op[i];
int d = gcd(a, n);
ans += (n - d) * c;
n = d;
if (n == 1)
break;
}
cout << (n == 1 ? ans : -1) << endl;
return 0;
}