Codeforces 1627D Not Adding 题解 [ 绿 ] [ 最大公约数 ] [ 调和级数 ] [ 枚举 ]

Not Adding:一个 \(\gcd\) 的经典 trick。

对于求 \(\gcd\) 的题目,有一个经典 trick:枚举 \(\gcd\) 的值,然后枚举 \(\gcd\) 的每个倍数,计算每个倍数对 \(\gcd\) 的贡献。不难发现这个复杂度是调和级数的。

对于此题,题意可以转化为求最终能生成多少个数。因此我们枚举 \(\gcd\) 以及它的倍数,发现当所有倍数的 \(\gcd\) 等于它本身时,这个数可以被生成。于是直接调和级数复杂度枚举即可。时间复杂度 \(O(V\log V)\),注意本题复杂度不是 \(O(V\log^2 V)\),因为 \(\gcd\) 具有均摊性质,枚举每个 \(\gcd\) 的时候最多只会变化 \(\log V\) 次。

#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 = 1000005;
int n, a[N], tot;
bitset<N> vis;
int gcd(int a, int b)
{
    if(b == 0) return a;
    return gcd(b, a % b);
}
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        vis[a[i]] = 1;
    }
    for(int i = 1; i < N; i++)
    {
        ll g = -1;
        for(int j = i; j < N; j += i)
        {
            if(vis[j])
            {
                if(g == -1) g = j;
                else g = gcd(g, j);
            }
        }
        if(g == i) tot++;
    }
    cout << tot - n;
    return 0;
}
posted @ 2025-08-31 15:35  KS_Fszha  阅读(13)  评论(0)    收藏  举报