D. Counting Rhyme
D. Counting Rhyme
You are given an array of integers .
A pair of integers , such that , is called good, if there does not exist an integer () such that is divisible by and is divisible by at the same time.
Please, find the number of good pairs.
Input
Each test contains multiple test cases. The first line contains the number of test cases (). The description of the test cases follows.
The first line of each test case contains a single integer ().
The second line of each test case contains integers ().
It is guaranteed that the sum of over all test cases does not exceed .
Output
For each test case, output the number of good pairs.
Example
input
6
4
2 4 4 4
4
2 3 4 4
9
6 8 9 4 6 8 9 4 9
9
7 7 4 4 9 9 6 2 9
18
10 18 18 15 14 4 5 6 8 9 10 12 15 16 18 17 13 11
21
12 19 19 18 18 12 2 18 19 12 12 3 12 12 12 18 19 16 18 19 12
output
0
3
26
26
124
82
Note
In the first test case, there are no good pairs.
In the second test case, here are all the good pairs: , , and .
解题思路
对于数对 ,如果存在某个 使得 且 ,意味着 是 和 的公倍数,并且 。为此我们可以根据数对的 来进行分类,那么对于所有 是 的数对,如果数组中存在某个 ,那么这些数对都不能被作为答案统计。
现在来求最大公约数为 的数对的数量。很明显要组成数对的元素必然是 的倍数,那么这些元素的数量就等于 ,其中 表示数组 中元素 的个数,。那么从这些元素中任意选择两个组合,就会有 个数对,但事实上这些数对的最大公约数不一定是 ,比如 ,其最大公约数应该为 。
因此 本质上是所有最大公约数是 的倍数的数对的数量。对此我们再定义 表示最大公约数确切为 的数对的数量,所以就有 ,推出 。所以我们可以从大到小倒着枚举 来求 ,此时 都已经算出来了,这一步的时间复杂度为 。
最后再枚举 中出现过的元素 ,以及 的倍数,那么最大公约数是 的倍数的数对都被筛掉,这一步的时间复杂度也是 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
int cnt[N];
LL f[N];
bool vis[N];
void solve() {
int n;
scanf("%d", &n);
memset(cnt, 0, n + 10 <<2);
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
cnt[x]++;
}
for (int i = n; i; i--) {
int t = 0;
for (int j = i; j <= n; j += i) {
t += cnt[j];
}
f[i] = t * (t - 1ll) >> 1;
for (int j = i + i; j <= n; j += i) {
f[i] -= f[j];
}
}
memset(vis, 0, n + 10);
for (int i = 1; i <= n; i++) {
if (cnt[i]) {
for (int j = i; j <= n; j += i) {
vis[j] = true;
}
}
}
LL ret = 0;
for (int i = 1; i <= n; i++) {
if (!vis[i]) ret += f[i];
}
printf("%lld\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round #904 (Div. 2) Editorial:https://codeforces.com/blog/entry/121618
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17796371.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效