[数论][莫队][莫比乌斯反演] hdu 4676 Sum Of Gcd

Description

给定一个 n 排列,q次询问,每次询问 Li<jRgcd(ai,aj)

Solution

首先先推一推柿子:

Li<jR(ai,aj)==Li<jRd|(ai,aj)dk|(aid,ajd)μ(k)Li<jRk|(ai,aj)d|kμ(kd)d
由莫比乌斯反演
id=φ1φ=μid
所以
Li<jR(ai,aj)=Li<jRk|(ai,aj)φ(k)
设约数 k 出现次数为ck,那么答案就是
kφ(k)(d|ici)(d|jcj)=kφ(k)(d|ici)2
这个柿子用莫队暴力维护一下就好啦。
时间复杂度是 O(n32+nlnn) 的吧。

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

const int N = 20202;
typedef long long ll;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
inline void read(int &x) {
    static char c; x = 0;
    for (c = get(); c < '0' || c > '9'; c = get());
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

int test, n, L, R, Pcnt, x, q, B;
ll res;
int a[N], c[N];
int prime[N], phi[N], vis[N];
int bl[N], pre[N];
ll ans[N];
struct Qry {
    int l, r, id;
    inline friend bool operator <(const Qry &a, const Qry &b) {
        return bl[a.l] == bl[b.l] ? a.r < b.r : bl[a.l] < bl[b.l];
    }
};
vector<int> fac[N];
Qry Q[N];

inline void Add(int pos, int x) {
    static int fact, key; key = a[pos];
    for (int i = 0; i < fac[key].size(); i++) {
        fact = fac[key][i];
        res -= (ll)phi[fact] * c[fact] * c[fact];
        c[fact] += x;
        res += (ll)phi[fact] * c[fact] * c[fact];
    }
}

int main(void) {
    read(test); phi[1] = 1;
    for (int i = 2; i <= 20000; i++) {
        if (!vis[i]) {
            prime[++Pcnt] = i; phi[i] = i - 1;
        }
        for (int j = 1; j <= Pcnt && (x = i * prime[j]) <= 20000; j++) {
            vis[x] = 1;
            if (i % prime[j]) {
                phi[x] = phi[i] * phi[prime[j]];
            } else {
                phi[x] = phi[i] * prime[j];
                break;
            }
        }
    }
    for (int i = 1; i <= 20000; i++)
        for (int j = i; j <= 20000; j += i)
            fac[j].push_back(i);
    for (int itisalongname = 1; itisalongname <= test; itisalongname++) {
        memset(c, 0, sizeof c);
        read(n); B = ceil(sqrt(n));
        for (int i = 1; i <= n; i++) {
            read(a[i]);
            pre[i] = pre[i - 1] + a[i];
        }
        read(q);
        for (int i = 1; i <= q; i++) {
            read(Q[i].l); read(Q[i].r);
            Q[i].id = i;
        }
        for (int i = 1; i <= n; i++)
            bl[i] = (i - 1) / B + 1;
        sort(Q + 1, Q + q + 1);
        L = 1; R = 0; res = 0;
        for (int i = 1; i <= q; i++) {
            while (R < Q[i].r) Add(++R, 1);
            while (R > Q[i].r) Add(R--, -1);
            while (L > Q[i].l) Add(--L, 1);
            while (L < Q[i].l) Add(L++, -1);
            ans[Q[i].id] = (res - pre[R] + pre[L - 1]) >> 1;
        }
        printf("Case #%d:\n", itisalongname);
        for (int i = 1; i <= q; i++) printf("%lld\n", ans[i]);
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值