D. Counting Rhyme

D. Counting Rhyme

You are given an array of integers a1,a2,,an.

A pair of integers (i,j), such that 1i<jn, is called good, if there does not exist an integer k (1kn) such that ai is divisible by ak and aj is divisible by ak 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 t (1t2104). The description of the test cases follows.

The first line of each test case contains a single integer n (1n106).

The second line of each test case contains n integers a1,a2,,an (1ain).

It is guaranteed that the sum of n over all test cases does not exceed 106.

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: (1,2), (2,3), and (2,4).

 

解题思路

  对于数对 (ai,aj),如果存在某个 ak 使得 akaiakaj,意味着 akaiaj 的公倍数,并且 akgcd(ai,aj)。为此我们可以根据数对的 gcd 来进行分类,那么对于所有 gcdd 的数对,如果数组中存在某个 akd,那么这些数对都不能被作为答案统计。

  现在来求最大公约数为 d 的数对的数量。很明显要组成数对的元素必然是 d 的倍数,那么这些元素的数量就等于 s=cd+c2d+ckd,其中 cx 表示数组 a 中元素 x 的个数,k=nd。那么从这些元素中任意选择两个组合,就会有 Cs2 个数对,但事实上这些数对的最大公约数不一定是 d,比如 (4d,6d),其最大公约数应该为 2d

  因此 Cs2 本质上是所有最大公约数是 d 的倍数的数对的数量。对此我们再定义 fd 表示最大公约数确切为 d 的数对的数量,所以就有 Cs2=i=1kfid,推出 fd=Cs2f2dfkd。所以我们可以从大到小倒着枚举 d 来求 fd,此时 f2d,,fkd 都已经算出来了,这一步的时间复杂度为 O(i=1nni)O(nlogn)

  最后再枚举 a 中出现过的元素 ai,以及 ai 的倍数,那么最大公约数是 ai 的倍数的数对都被筛掉,这一步的时间复杂度也是 O(i=1nni)O(nlogn)

  AC 代码如下,时间复杂度为 O(nlogn)

#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

posted @   onlyblues  阅读(104)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示