BZOJ 4659: Lcm

本文深入探讨了一种复杂的数学问题,即求解特定条件下的双层求和公式,通过引入数学技巧和算法优化,将原始问题转换为更易处理的形式,并提供了两种不同复杂度的解法,包括O(n*log_n)和O(n)的算法实现。

\[ 题意:求\sum_{i = 1} ^ A \sum_{j = 1} ^ B lcm(i, j) \cdot \mu ^ 2(gcd(i, j)) \]

\[ \begin{eqnarray*} 题目所求的是\sum_{i = 1} ^ A \sum_{j = 1} ^ B lcm(i, j) \cdot \mu ^ 2(gcd(i, j)) \nonumber \\ 我们发现\\ Ans & = & \sum_{i = 1} ^ A \sum_{j = 1} ^ B lcm(i, j) \cdot \mu ^ 2(gcd(i, j)) \nonumber \\ & = & \sum_{i = 1} ^ A \sum_{j = 1} ^ B \sum_{d \mid i, d \mid j} [gcd(i, j) == d] \frac{i \cdot j}{d} \cdot \mu ^ 2(d) \nonumber \\ & =& \sum_{d = 1} ^ A \sum_{i = 1} ^ {\lfloor \frac{A}{i} \rfloor} \sum_{j = 1} ^ {\lfloor \frac{B}{j} \rfloor} [gcd(i, j) == 1] \cdot d \cdot i \cdot j \cdot \mu ^ 2(d) \nonumber \\ & = &\sum_{d=1} ^ A \mu ^ 2(d) \cdot d \sum_{i = 1} ^ {\lfloor \frac{A}{d} \rfloor} i \sum_{ j = 1} ^ {\lfloor \frac{B}{d} \rfloor} j \sum_{e \mid i, e \mid j} \mu(e) \nonumber \\ & = &\sum_{d=1} ^ A \mu ^ 2(d) \cdot d \sum_{e = 1} ^ {\lfloor \frac{A}{d} \rfloor} \mu(e) \cdot e ^ 2 \sum_{i = 1} ^ {\lfloor \frac{A}{d \cdot e} \rfloor} i \sum_{j = 1}^{\lfloor \frac{B}{d \cdot e} \rfloor} j \nonumber \\ & = &\sum_{D=1}^AD\sum_{d \mid D}\mu ^2 (d)\cdot \mu(\frac{D}{d})\cdot \frac{D}{d}\cdot sum(\lfloor \frac{A}{D} \rfloor) \cdot sum(\lfloor \frac{B}{D} \rfloor) \\ 发现f(x)=\sum_{d \mid x} g(x) \cdot h(\frac{x}{d})\\ g(x)为极性函数\\ h(x)为极性函数\\ 所以f(x)为积性函数 \end{eqnarray*} \]

\[ O(n\cdot log_n)解法 \]

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const double eps = 1e-8;
const int MOD = 1 << 30;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const int maxn = 4e6 + 10;

bool check[maxn];
int prime[maxn];
int mu[maxn];
int f[maxn];

void Moblus()
{
    mu[1] = 1;
    int tot = 0;
    for (int i = 2; i < maxn; ++i)
    {
        if (!check[i])
        {
            prime[tot++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < tot; ++j)
        {
            if (i * prime[j] >= maxn) break;
            check[i * prime[j]] = true;
            if (i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                break;
            }
            else
            {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
    for (int i = 1; i < maxn; i++) if (mu[i] != 0)
    {
        for (int j = 1; j * i < maxn; j++)
        {
            int t = j * i;
            f[t] += i * j * j * mu[j];
        }
    }
    for (int i = 2; i < maxn; i++) f[i] += f[i - 1];
}

int n, m;

int SUM(int x)
{
    return x * (x + 1) / 2;
}

void RUN()
{
    Moblus();
    int t;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d %d", &n, &m);
        int ans = 0;
        for (int i = 1, la = 0; i <= min(n, m); i = la + 1)
        {
            la = min(n / (n / i), m / (m / i));
            ans += SUM(n / i)*SUM(m / i)*(f[la] - f[i - 1]);
        }
        printf("%d\n", (ans % MOD + MOD) % MOD);
    }
}

int main()
{
#ifdef LOCAL_JUDGE
    freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE

    RUN();

    return 0;
}

\[ O(n)解法 \]

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

const double eps = 1e-8;
const ll MOD = 1073741824;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const int maxn = 4e6 + 10;

bool check[maxn];
int prime[maxn];
unsigned int f[maxn];

void Moblus()
{
    f[1] = 1;
    int tot = 0;
    for (int i = 2; i < maxn; ++i)
    {
        if (!check[i])
        {
            prime[tot++] = i;
            f[i] = 1 - i;
        }
        for (int j = 0; j < tot; ++j)
        {
            if (i * prime[j] > maxn) break;
            check[i * prime[j]] = true;
            if (i % prime[j] == 0)
            {
                if (i % (prime[j] * prime[j]) == 0)
                {
                    f[i * prime[j]] = 0;
                }
                else
                {
                    f[i * prime[j]] = -f[i / prime[j]] * prime[j];
                }
                break;
            }
            f[i * prime[j]] = f[i] * (1 - prime[j]);
        }
    }
    for (int i = 2; i < maxn; ++i) f[i] = f[i] * i + f[i - 1];
}

ll n, m;

unsigned int SUM(int x)
{
    return (unsigned int)x * (x + 1) / 2;
}

void RUN()
{
    Moblus();
    int t;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%lld %lld", &n, &m);
        if (n > m) swap(n, m);
        unsigned int ans = 0;
        for (int i = 1, la = 0; i <= n; i = la + 1)
        {
            la = min(n / (n / i), m / (m / i));
            ans += (f[la] - f[i - 1]) * SUM(n / i) * SUM(m / i);
        }
        printf("%u\n", ans % MOD);
    }
}

int main()
{
#ifdef LOCAL_JUDGE
    freopen("Text.txt", "r", stdin);
#endif // LOCAL_JUDGE

    RUN();

    return 0;
}

转载于:https://www.cnblogs.com/Xue37/p/10700061.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值