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
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18269893