码迷,mamicode.com
首页 > 其他好文 > 详细

【Luogu P2257】YY 的 GCD

时间:2019-01-06 19:20:35      阅读:193      评论:0      收藏:0      [点我收藏+]

标签:return   ref   floor   splay   lin   +=   bre   测试   const   

题目

求:

\[ \sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) \in \mathbb P] \]

\(T\) 组数据, \(T\le 10^4, n, m\le 10^7\)

分析

莫比乌斯反演:

\[ \begin{align*} & \sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) \in \mathbb P]\ = & \sum_{p \in \mathbb P, p\le \min(n, m)}\sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) = p]\\end{align*} \]

\(f(x) = \sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) = x]\), $F(x) = \sum_{i = 1}^n \sum_{j = 1}^m [x?|\gcd(i, j)]=\left\lfloor \frac nx\right\rfloor\left\lfloor \frac mx\right\rfloor $

则有:
\[ F(x) = \sum_{x|d, d\le \min(n, m)} f(d) \Rightarrow f(x) = \sum_{x|d, d\le\min(n, m)} \mu(\frac dx)F(d) \]
故有:
\[ \begin{align*} & \sum_{p \in \mathbb P, p\le \min(n, m)}\sum_{i = 1}^n \sum_{j = 1}^m [\gcd(i, j) = p]\ = & \sum_{p \in \mathbb P, p\le \min(n, m)} f(p) \ = & \sum_{p \in \mathbb P, p\le \min(n, m)} \sum_{p|d,d\le \min(n, m)} \mu(\frac dp) \left\lfloor \frac nd\right\rfloor\left\lfloor \frac md\right\rfloor \ = & \sum_{d = 1}^{\min(n, m)} \sum_{p \in \mathbb P,p|d, p\le \min(n, m)} \mu(\frac dp) \left\lfloor \frac nd\right\rfloor\left\lfloor \frac md\right\rfloor \ = & \sum_{d = 1}^{\min(n, m)} \left\lfloor \frac nd\right\rfloor\left\lfloor \frac md\right\rfloor \sum_{p \in \mathbb P,p|d, p\le \min(n, m)} \mu(\frac dp) \end{align*} \]
设 $g(x) = \sum_{p \in \mathbb P,p|x, p\le \min(n, m)} \mu(\frac xp) $

求法(暴力出奇迹, 经测试下面算法耗时不到 \(0.5 s\)):

void get_g(int n) {
    for(int i = 1; i <= n; i++) {
        int tmp = i;
        while(tmp != 1) {
            g[i] += mobius[i / s_factor[tmp]];
            int p = s_factor[tmp];
            if(tmp % (p * p) == 0) break;
            for(; tmp % p == 0; tmp /= p);
        }
        g_prefix[i] = g_prefix[i - 1] + g[i];
    }
}

有上式等于:
\[ \sum_{d = 1}^{\min(n, m)} \left\lfloor \frac nd\right\rfloor\left\lfloor \frac md\right\rfloor g(d) \]
对于 \(\left\lfloor \frac nd\right\rfloor\left\lfloor \frac md\right\rfloor\) 相同的值整除分块即可.

代码

#include <bits/stdc++.h>

typedef long long Int64;

const int kMaxSize = 1e7 + 5;

int s_factor[kMaxSize], prime[kMaxSize], mobius[kMaxSize], g[kMaxSize],
    g_prefix[kMaxSize], prime_tot = 0;
bool isn_prime[kMaxSize];

void euler_sieve(int n) {
    mobius[1] = 1;
    isn_prime[0] = isn_prime[1] = true;
    for(int i = 2; i <= n; i++) {
        if(!isn_prime[i]) {
            prime[prime_tot++] = i;
            s_factor[i] = i;
            mobius[i] = -1;
        }
        for(int j = 0; j < prime_tot && i * prime[j] <= n; j++) {
            isn_prime[i * prime[j]] = true;
            s_factor[i * prime[j]] = prime[j];
            mobius[i * prime[j]] = -mobius[i];
            if(i % prime[j] == 0) {
                mobius[i * prime[j]] = 0;
                break;
            }
        }
    }
}

void get_g(int n) {
    for(int i = 1; i <= n; i++) {
        int tmp = i;
        while(tmp != 1) {
            g[i] += mobius[i / s_factor[tmp]];
            int p = s_factor[tmp];
            if(tmp % (p * p) == 0) break;
            for(; tmp % p == 0; tmp /= p);
        }
        g_prefix[i] = g_prefix[i - 1] + g[i];
    }
}

int main() {
    euler_sieve(1e7);
    get_g(1e7);
    int t;
    scanf("%d", &t);
    while(t--) {
        int n, m;
        Int64 ans = 0;
        scanf("%d%d", &n, &m);
        if(!n) break;
        for(int l = 1, r; l <= n && l <= m; l = r + 1) {
            r = std::min(n / (n / l), m / (m / l));
            ans += (Int64)(n / l) * (m / l) * (g_prefix[r] - g_prefix[l - 1]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

【Luogu P2257】YY 的 GCD

标签:return   ref   floor   splay   lin   +=   bre   测试   const   

原文地址:https://www.cnblogs.com/zhylj/p/10229884.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!