CF1884D Counting Rhyme
题目翻译:
给定长度为 的序列 。对于 ,若不存在 使得 且 那么 是好的。求出好的数对数量。。
注意到若 且 ,那么 一定也能整除 。那么我们可以通过枚举 ,找出最大公约数为 的数对的个数 ,最后答案即为 。
下面考虑计算 。这里我们用 表示数字 在序列 中出现的次数,用 表示能被 整除的数的个数。显然,。由此易知,所有最大公约数能被 整除的数对个数为 ,接下来还需要减去最大公约数大于 的情况,其实就是 。即:
注意到这个过程比较像埃氏筛,所以时间复杂度应该是 。但是官方题解写的是 ,我也不知道到底哪个是对的,反正能过就行了。
代码
记得开 long long 。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using namespace std;
typedef long long ll;
int T;
const int maxn = 1e6 + 5;
int n;
int a[maxn], cnt[maxn], vis[maxn];
int f[maxn];
void Solve()
{
cin >> n;
for (int i = 1; i <= n; i++)
f[i] = cnt[i] = vis[i] = 0;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cnt[a[i]]++;
for (int i = 1; i <= n; i++)
vis[a[i]] = 1;
for (int i = n; i >= 1; i--)
{
int sum = 0;
for (int j = i; j <= n; j += i)
{
sum += cnt[j];
f[i] -= f[j];
if (vis[i])
vis[j] = 1;
}
f[i] += sum * (sum - 1) / 2;
}
int ans = 0;
for (int i = 1; i <= n; i++)
if (!vis[i])
ans += f[i];
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> T;
while (T--)
{
Solve();
}
return 0;
}