题意
传送门 Codeforces 1986 G2 Permutation Problem (Hard Version)
题解
令 d = g c d ( p [ i ] , i ) d=gcd(p[i],i) d=gcd(p[i],i),且 a [ i ] = i / d , b [ i ] = p [ i ] / d a[i] = i / d, b[i] = p[i] / d a[i]=i/d,b[i]=p[i]/d。此时 a [ i ] , b [ i ] a[i],b[i] a[i],b[i]互质,则满足条件的 i , j i,j i,j需要满足 a [ j ] ∣ b [ i ] , a [ i ] ∣ b [ j ] a[j]\mid b[i], a[i] \mid b[j] a[j]∣b[i],a[i]∣b[j]。
固定 a [ i ] a[i] a[i],枚举 b [ j ] = k × a [ i ] b[j] = k\times a[i] b[j]=k×a[i],由于不同的 a [ i ] a[i] a[i]只处理一次,均摊时间复杂度 O ( ( ∑ n / i ) / n ) = O ( log n ) O\Big((\sum n/i)/n\Big)=O(\log n) O((∑n/i)/n)=O(logn)。对于每一个 b [ j ] b[j] b[j],将所有 a [ j ] a[j] a[j]按照值域用一个计数数组维护; b [ j ] b[j] b[j]仅被其因子扫描到,而由于 b b b不一定是一个排列,则每一个 b [ j ] b[j] b[j]被扫描的次数为 O ( M ) O(M) O(M), M M M为 [ 1 , 5 × 1 0 5 ] [1, 5\times 10^{5}] [1,5×105]内数字的最大因子数。最后,枚举 a [ i ] a[i] a[i]对应的所有 b [ i ] b[i] b[i],再枚举其因子,并根据计数数组统计答案,均摊时间复杂度也是 O ( log n ) O(\log n) O(logn)。总时间复杂度 O ( n ( log n + M ) ) O\Big(n(\log n + M)\Big) O(n(logn+M))。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt;
cin >> tt;
while (tt--) {
int n;
cin >> n;
long long res = 0;
vector<vector<int>> pos(n + 1), val(n + 1);
for (int i = 1; i <= n; ++i) {
int p;
cin >> p;
int d = gcd(p, i);
pos[p / d].push_back(i / d);
val[i / d].push_back(p / d);
if (i / d == 1) {
res -= 1;
}
}
vector<vector<int>> fac(n + 1);
for (int i = 1; i <= n; ++i) {
for (int j = i; j <= n; j += i) {
fac[j].push_back(i);
}
}
vector<int> cnt(n + 1);
for (int p = 1; p <= n; ++p) {
if (val[p].empty()) {
continue;
}
for (int v = p; v <= n; v += p) {
for (int q : pos[v]) {
cnt[q] += 1;
}
}
for (int v : val[p]) {
for (int d : fac[v]) {
res += cnt[d];
}
}
for (int v = p; v <= n; v += p) {
for (int q : pos[v]) {
cnt[q] -= 1;
}
}
}
res /= 2;
cout << res << '\n';
}
return 0;
}