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 n5105 and the sum of n for all sets of input data does not exceed 5105.

You are given a permutation p of length n. Calculate the number of index pairs 1i<jn such that pipj is divisible by ij 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 (1t104) — 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 (1n5105) — the length of the permutation p.

The second line of each set of input data contains n distinct integers p1,p2,,pn (1pin) — the permutation p.

It is guaranteed that the sum of n for all sets of input data does not exceed 5105.

Output

For each set of input data, output the number of index pairs 1i<jn such that pipj is divisible by ij 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 需要满足 ijpipj,为了避免枚举 j,令 xi=igcd(i,ai)yi=aigcd(i,ai)。那么 ijpipj 就等价于 xiyjxjyi

  因此一种枚举的方法是,固定 i 后枚举 xi 的倍数 kxi,kZ+,统计 yj=kxi 中有多少个相应的 xj 满足 xjyi。但为了保证时间复杂度,我们不枚举 i,而是从 1n 枚举 xi。同时开两个 std::vector pqp[xi] 表示所有 xi 对应的 yiq[yi] 表示所有 yi 对应的 xi

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

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

  再考虑枚举所有 p[xi] 内的元素的因子的次数,本质就是枚举 1n 中每个数的因子,也是 O(nlogn) 级别。

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

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

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