Codeforces 1986 G2 数论 + 暴力

题意

传送门 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;
}
  • 27
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值