信息学奥赛一本通1713:生成树
题目描述】
给定一张N个点M条边的无向图,求图中所有生成树边权最大公约数的最小公倍数。
【输入】
第一行有两个整数N,M,分别表示点数和边数。
接下来M行每行有三个整数si,ti,di,表示点si与点ti间存在一条权为di的边,保证si不等于ti。
【输出】
所求的最小公倍数ans。
输入样例】
3 3
1 2 2
2 3 3
1 3 6
【输出样例】
6
\(N≤1000,M≤100000,di≤2^{15}−1,ans≤2^{64}−1\)
这题题干很优雅啊!
首先,我们直接做发现很难,那就可以通过贡献的方法解决。
具体的,我们可以对ans质因数分解,变成\(∏p_{i}^{a_{i}}\)的形式,因为他是一个lcm的值,所以他的所有质因数,都是由所有边权的质因数贡献的
那么就好办了,直接计算每个\(p_{i}\)的贡献,也就是\(a_{i}\),最后一乘不就完事了吗?而且还自动地把可能会重复计数的问解决了,那么问题就只有一个了,怎么计算贡献。
因为我们只需要计算\(p_{i}\)的贡献,那么我们其实可以当其他数的都不存在,只有\(p\)的幂。这时,你要算最大公因数,既然都是最大的了,不就是找一个生成树,然后让他的最小值最大吗?
这里虽然很像二分,但是其实只需要排个序,然后从大到小加边,如果某一个时刻整个图联通了,那么这个边的边权就是最大的最小边。
复杂度如果有P个质数,那么就是\(O(PM+MlogM)\)
code:
#include <iostream>
#include <algorithm>
#include <cstring>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
const int N = 1001;
const int M = 100001;
const int INF = 0x3f3f3f3f;
int read() {
int k = 0, f = 1;char c = getchar();
for (;c < '0' || c>'9';c = getchar())if (c == '-')f = -1;
for (;c >= '0' && c <= '9';c = getchar())k = (k << 1) + (k << 3) + (c ^ 48);
return k * f;
}
struct edge {
int u, v, w;
bool operator<(const edge& a)const {
return w < a.w;
}
}edges[M];
bool is_p[1 << 16];
vector<int>prim;
int cnt[1 << 16];
int n, m;
bool vis[N];
vector<edge>E[1 << 16];
void init() {
memset(is_p, true, sizeof(is_p));
is_p[0] = is_p[1] = false;
for (int i = 2;i < (1 << 15);i++) {
if (is_p[i])prim.push_back(i);
for (int p : prim) {
if (p * i > (1 << 15))break;
is_p[p * i] = false;
if (i % p == 0)break;
}
}
}
int fast_pow(int a, int b) {
int ret = 1;
while (b) {
if (b & 1)ret *= a;
a *= a;
b >>= 1;
}
return ret;
}
signed main() {
init();
n = read(), m = read();
for (int i = 0;i < m;i++) {
edges[i].u = read(), edges[i].v = read(), edges[i].w = read();
int temp = edges[i].w;
for (int p : prim) {
if (p*p > temp)break;//一定要加,不然会超时
if (temp % p == 0) {
int cnt2 = 0;
cnt[p]++;
while (temp % p == 0)cnt2++, temp /= p;
E[p].push_back({ edges[i].u,edges[i].v,cnt2 });
}
}
if (temp > 1)cnt[temp]++, E[temp].push_back({ edges[i].u,edges[i].v,1 });
}
for (int p : prim) {
if (cnt[p] < n - 1)continue;
sort(E[p].begin(), E[p].end());
}
int ans = 1;
for (int p : prim) {
if (cnt[p] < n - 1)continue;//枝剪,加不加都行
memset(vis, false, sizeof(vis));
int cnt2 = 0, i = E[p].size() - 1;
for (;i >= 0;i--) {
if (!vis[E[p][i].u])cnt2++, vis[E[p][i].u] = true;
if (!vis[E[p][i].v])cnt2++, vis[E[p][i].v] = true;
if (cnt2 == n)break;
}
if (cnt2 < n)continue;
ans *= fast_pow(p, E[p][i].w);
}
cout << ans;
}

浙公网安备 33010602011771号