Curious
题意
有T组数据,n, m, k, 然后有n个数字,有k次查询,每次查询以一个x, 问\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}{gcd}{(ai, aj) == x}的个数有多少
思路:
首先暴力的话时间复杂度为{O}({n}^{2})时间肯定是过不去的,然后想这里有gcd, 而且是对整个范围进行的求,非常的像莫比乌斯反演,那就可以向这个方向考虑下,首先令f({d}) = \sum\limits_{i=1}\limits^{n}\sum\limits_{j=1}^{n}gcd({ai, aj} == d), 最后只要求这个f(d), 这个就是答案,求这个可以利用一下莫比乌斯反演,令F(x) = \sum\limits_{d | x}, F(x) = \sum\limits_{d | x}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}{gcd}{(ai, aj) == x}, 然后想gcd(ai, aj) == x, d | x, 求这个的数量就等价于{d | ai } ^ {d | aj} 的数量,可以设h(x)为{x | ai}, 在a数组中有几个是他的倍数,这个可以预处理出来,预处理出来之后,利用莫比乌斯反演f(d) = \sum\limits_{d | x}{h(x)}^{2}\mu(\frac{x}{d}), 接下来就是求这个式子即可,求这个式子就交给代码功底了
总结:
求这种gcd问题一般都可以用往莫比乌斯反演上靠一靠,然后疯狂推式子,最后利用莫比乌斯反演求解,求解这个莫比乌斯反演可以换元求解,如果数据量不能接受暴力,dp + 换元
#include <bits/stdc++.h>
#define endl '\n'
#define IOS ios::sync_with_stdio(false);
using namespace std;
typedef long long ll;
const ll MAXN = 1e5 + 10;
ll T, n, m, k;
ll a[MAXN];
bool vis[MAXN]; //vis标记质数
ll prime[MAXN], mu[MAXN], h[MAXN], exist[MAXN], dp[MAXN]; //分别表示质数,mu, h(x), 存在的个数,dp[i]的值是答案
void Init()
{
for (int i = 1; i <= m; ++i)
{
exist[i] = 0;
}
for (int i = 1; i <= m; ++i)
{
dp[i] = -1;
}
}
void get_mu()
{
ll cnt = 0;
mu[1] = 1;
for (int i = 2; i < MAXN; ++i) //线性筛法求mu
{
if (!vis[i])
vis[i] = true, prime[++cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && prime[j] * i < MAXN; ++j)
{
vis[i * prime[j]] = true;
if (i % prime[j] == 0)
break;
else
mu[i * prime[j]] = -mu[i];
}
}
}
vector<ll> v[MAXN];
void get_factor() //求每一个数的因数
{
for (int i = 1; i < MAXN; ++i)
{
for (int j = 1; j <= sqrt(i); ++j)
{
if (i % j == 0)
{
v[i].push_back(j);
if (j * j != i)
v[i].push_back(i / j);
}
}
}
}
int main()
{
IOS; cin.tie(0), cout.tie(0);
get_mu(); //OK
get_factor(); //OK
cin >> T;
while (T--)
{
cin >> n >> m >> k;
Init();
for (int i = 1; i <= n; ++i)
{
cin >> a[i];
++exist[a[i]];
}
while (k--)
{
ll x;
cin >> x;
if (dp[x] != -1)
{
cout << dp[x] << endl;
continue;
}
ll len = 0;
for (int i = 1; i <= m; ++i) //记录最大的倍数
{
ll X = i * x;
if (i * x > m)
break;
len = i;
for (auto it : v[i]) //考虑每一个X对h(x)的贡献,然后不断的进行+即可,转换为求倍数的形式
{
h[it] += exist[X];
}
}
ll ans = 0;
for (int i = 1; i <= len; ++i)
{
ans += h[i] * h[i] * mu[i];
h[i] = 0;
}
dp[x] = ans;
cout << dp[x] << endl;
}
}
return 0;
}