序列的GCD的生成集
一个序列的GCD的生成集为:
存在一个正整数集合 S S S,该集合的GCD生成集为:
S gcd = { a ∣ a = gcd ( x 1 , x 2 , … , x n ) ∣ n > 0 , x 1 , x 2 , … , x n ∈ S } S_{\gcd} = \{ a | a = \gcd(x_{1},x_{2},\ldots,x_{n}) | n >0, x_{1},x_{2},\ldots,x_{n} \in S\} Sgcd={a∣a=gcd(x1,x2,…,xn)∣n>0,x1,x2,…,xn∈S}
即在集合 S S S中任意非空的子集,其所有元素构成的最大公约数。
序列的GCD的生成集的大小
求 ∣ S gcd ∣ |S_{\gcd}| ∣Sgcd∣的值。
对于可以表示出的最大公约数 a a a来说,其生成 a a a的子序列中的元素一定都是 a a a的倍数,如果不是 a a a的倍数,那么最大公约数不可能是 a a a。第二,可以表示出的最大公约数 a a a,当且仅当 S S S中所有 a a a的倍数的子集的最大公约数是 a a a。
必要性:
假设我们已经选取了 S S S中所有 a a a的倍数的子集中的一些元素,现在的最大公约数不是 a a a,再往集合中添加元素,一直到把所有的 S S S中所有 a a a的倍数的子集中的元素都添加进来,此时最大公约数不会增大,也不会小于 a a a。如果最大公约数大于 a a a,那么就不可能生成 a a a(反证:如果存在生成 a a a的序列,那么必定是 a a a倍数集合的子集,最后,最大公约数反而增大了,不可能),如果最大公约数正好等于 a a a,那就是 a a a。
充分性:
如果不是 a a a,那么绝对不可能比 a a a小,只能比 a a a大,如果存在生成 a a a的序列,那么必定是 a a a倍数集合的子集,最后,最大公约数反而增大了,不可能。
设数组 g [ y ] g[y] g[y] 表示当前遍历过的所有数中,所有 y y y 的倍数的最大公约数。
对于数组中的每个数 x x x,我们枚举其所有的约数 y y y,并使用 x x x 更新 g [ y ] g[y] g[y] 即可。
最终遍历数组 g g g,只要有 y = g [ y ] y = g[y] y=g[y],那么答案增加 1 1 1。
我们也可以直接枚举 y y y,然后枚举 y y y的倍数。
class Solution
{
public:
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int countDifferentSubsequenceGCDs(vector<int> &nums)
{
unordered_set<int> st;
int mx = 0;
for (int i : nums)
{
st.insert(i);
mx = max(mx, i);
}
int cnt = 0;
for (int y = 1; y <= mx; y++)
{
int g = 0;
for (int k = y; k <= mx; k += y)
{
if (st.count(k))
{
if (g == 0)
g = k;
else
g = gcd(g, k);
}
if(g == y)
{
cnt++;
break;
}
}
}
return cnt;
}
};
例题
AtCoder Beginner Contest 191 Problem F
我们发现,黑板上最终的数字必须小于等于黑板上最小的数字,而且黑板上最终的数字是黑板上某些元素的GCD。因此问题就转化成,求生成集中,小于等黑板上最小的数字的个数。
但是有一点不同,我们发现这道题元素个数少,元素值大,我们就不能枚举GCD了,我们只好选择求元素的约数了。
#include <bits/stdc++.h>
using namespace std;
#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)
typedef long long ll;
ll gcd(ll a, ll b)
{
return b == 0 ? a : gcd(b, a % b);
}
ll arr[2005];
int main()
{
int n;
scanf("%d", &n);
ll mx = 0, mi = LONG_LONG_MAX;
for (int i = 0; i < n; i++)
{
ll val;
scanf("%lld", &val);
mx = max(mx, val);
mi = min(mi, val);
arr[i] = val;
}
unordered_map<ll, ll> gcdmp;
for (int i = 0; i < n; i++)
{
ll tag = arr[i];
for (ll c = 1; c * c <= tag; c++)
{
if (tag % c == 0)
{
if (!gcdmp.count(c))
{
gcdmp[c] = tag;
}
else
{
gcdmp[c] = gcd(gcdmp[c], tag);
}
ll o = tag / c;
if (!gcdmp.count(o))
{
gcdmp[o] = tag;
}
else
{
gcdmp[o] = gcd(gcdmp[o], tag);
}
}
}
}
int ans = 0;
for (pair<ll, ll> p : gcdmp)
{
if (p.first == p.second && p.first <= mi)
{
ans++;
}
}
printf("%d", ans);
return 0;
}