G2. Permutation Problem (Hard Version)

G2. Permutation Problem (Hard Version)

This is the hard version of the problem. The only difference is that in this version $n \leq 5 \cdot 10^5$ and the sum of $n$ for all sets of input data does not exceed $5 \cdot 10^5$.

You are given a permutation $p$ of length $n$. Calculate the number of index pairs $1 \leq i < j \leq n$ such that $p_i \cdot p_j$ is divisible by $i \cdot j$ without remainder.

A permutation is a sequence of $n$ integers, in which each integer from $1$ to $n$ occurs exactly once. For example, $[1]$, $[3,5,2,1,4]$, $[1,3,2]$ are permutations, while $[2,3,2]$, $[4,3,1]$, $[0]$ are not.

Input

Each test consists of several sets of input data. The first line contains a single integer $t$ ($1 \leq t \leq 10^4$) — the number of sets of input data. Then follows their description.

The first line of each set of input data contains a single integer $n$ ($1 \leq n \leq 5 \cdot 10^5$) — the length of the permutation $p$.

The second line of each set of input data contains $n$ distinct integers $p_1, p_2, \ldots, p_n$ ($1 \leq p_i \leq n$) — the permutation $p$.

It is guaranteed that the sum of $n$ for all sets of input data does not exceed $5 \cdot 10^5$.

Output

For each set of input data, output the number of index pairs $1 \leq i < j \leq n$ such that $p_i \cdot p_j$ is divisible by $i \cdot j$ without remainder.

Example

input

6
1
1
2
1 2
3
2 3 1
5
2 4 1 3 5
12
8 9 7 12 1 10 6 3 2 4 11 5
15
1 2 4 6 8 10 12 14 3 9 15 5 7 11 13

output

0
1
1
3
9
3

Note

In the first set of input data, there are no index pairs, as the size of the permutation is $1$.

In the second set of input data, there is one index pair $(1, 2)$ and it is valid.

In the third set of input data, the index pair $(1, 2)$ is valid.

In the fourth set of input data, the index pairs $(1, 2)$, $(1, 5)$, and $(2, 5)$ are valid.

 

解题思路

  当固定 $i$ 后,合法的 $j$ 需要满足 $i \cdot j \mid p_i \cdot p_j$,为了避免枚举 $j$,令 $x_i = \frac{i}{\gcd(i,a_i)}$,$y_i = \frac{a_i}{\gcd(i,a_i)}$。那么 $i \cdot j \mid p_i \cdot p_j$ 就等价于 $x_i \mid y_j$ 且 $x_j \mid y_i$。

  因此一种枚举的方法是,固定 $i$ 后枚举 $x_i$ 的倍数 $k \cdot x_i, \, k \in \mathbb{Z^+}$,统计 $y_j = k \cdot x_i$ 中有多少个相应的 $x_j$ 满足 $x_j \mid y_i$。但为了保证时间复杂度,我们不枚举 $i$,而是从 $1$ 到 $n$ 枚举 $x_i$。同时开两个 std::vector $p$ 和 $q$,$p[x_i]$ 表示所有 $x_i$ 对应的 $y_i$,$q[y_i]$ 表示所有 $y_i$ 对应的 $x_i$。

  因此新的枚举方法是,固定 $x_i$,枚举 $x_i$ 的倍数 $k \cdot x_i$,枚举 $q[k \cdot x_i]$ 中的元素并用哈希表统计出现次数。最后枚举 $p[x_i]$ 中每个元素的因子 $d$,把哈希表中 $d$ 的出现次数累加到答案。

  看上去好像有多重循环的枚举,但实际上整个过程的时间复杂度是 $O(n \log{n})$。先考虑整个过程枚举 $q[k \cdot x_i]$ 中的元素的次数,容易知道 $q[k \cdot x_i]$ 内的每个元素最多会被枚举 $k \cdot x_i$ 的因子个数次,由于 $1 \sim n$ 每个数的因子的总和大概是 $O(n \log{n})$ 级别,因此平均每个数有 $O(\log{n})$ 个因子。所以 $q[k \cdot x_i]$ 内的每个元素最多会被枚举 $O(\log{n})$ 次,那么整个过程中所有元素会被枚举 $O(n \log{n})$ 次。

  再考虑枚举所有 $p[x_i]$ 内的元素的因子的次数,本质就是枚举 $1 \sim n$ 中每个数的因子,也是 $O(n \log{n})$ 级别。

  最后由于统计的对数是不考虑先后顺序的,因此每一对答案都会被统计两次,所以需要对统计的结果除以 $2$。另外需要注意的是,如果有 $i \mid a_i$,在统计的时候会把 $(i,i)$ 这一对也统计进来,因此需要一开始对答案减 $1$。

  AC 代码如下,时间复杂度为 $O(n \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 5e5 + 5;

int a[N];
vector<int> d[N];
vector<int> p[N], q[N];
int cnt[N];

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        d[i].clear();
        p[i].clear();
        q[i].clear();
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j += i) {
            d[j].push_back(i);
        }
    }
    LL ret = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        if (a[i] % i == 0) ret--;
        int d = __gcd(i, a[i]);
        int x = i / d, y = a[i] / d;
        p[x].push_back(y);
        q[y].push_back(x);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j += i) {
            for (auto &x : q[j]) {
                cnt[x]++;
            }
        }
        for (auto &x : p[i]) {
            for (auto &y : d[x]) {
                ret += cnt[y];
            }
        }
        for (int j = i; j <= n; j += i) {
            for (auto &x : q[j]) {
                cnt[x] = 0;
            }
        }
    }
    printf("%lld\n", ret / 2);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Codeforces Round 954 (Div. 3) Editorial:https://codeforces.com/blog/entry/130762

  Codeforces Round 954 (Div. 3) A - G:https://zhuanlan.zhihu.com/p/705010951

posted @ 2024-06-26 20:50  onlyblues  阅读(27)  评论(0编辑  收藏  举报
Web Analytics