nju1017 Heresy

本文介绍了一道关于数论的题目,通过莫比乌斯反演解决求解特定数学表达式的问题,并给出了详细的解题思路及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题网址:http://acm.nju.edu.cn:8000/problem.php?id=1017
原题大意:求 ni=1mj=1i2j2gcd(i,j)
和BZOJ2154这类题目相似,都是莫比乌斯反演。
F(n,m)=ni=1mj=1i2j2[gcd(i,j)=1]
则由 d|nμ(d)=[n=1] 可知
F(n,m)=ni=1i2mj=1j2d|gcd(i,j)μ(d)=μ(d)1in and d|ii21jm and d|jj2=μ(d)d4[nd]i=1i2[md]j=1j2=μ(d)d4Sum([nd],[md])
其中 Sum(i,j)=16i(i+1)(2i+1)16j(j+1)(2j+1)
我们枚举 gcd(i,j)=d ,则有
ans=min(n,m)d=1d5F([nd],[md])=min(n,m)d=1d5min([nd],[md])i=1μ(i)i4Sum([ndi],[mdi])

5次方的数据做的时候总是出问题,不能直接用公式求和,最后只能设数组记录数值才过的

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>

using namespace std;
const int N = 1000010;
#define ll long long
const int MOD = 1000000007;
bool check[N + 2];
int prime[N + 2];
int mu[N + 2];
ll s[N + 2];
ll ssum[N + 2];

void Moblus(){
    memset(check, false, sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for (int i = 2; i <= N; i++){
        if (!check[i]){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j<tot; j++){
            if (i*prime[j]>N) break;
            check[i*prime[j]] = true;
            if (i%prime[j] == 0){
                mu[i*prime[j]] = 0;
                break;
            }
            else{
                mu[i*prime[j]] = -mu[i];
            }
        }
    }
    s[0] = 0;
    ssum[0] = 0;
    for (ll i = 1; i <= N; i++){
        s[i] = ((s[i - 1] + (1ll * mu[i] * i%MOD*i%MOD*i%MOD*i%MOD)) % MOD + MOD) % MOD;
        ssum[i] = (ssum[i-1] + i*i%MOD*i%MOD*i%MOD*i%MOD)%MOD;
    }
}

ll sum(ll a, ll b){
    ll x = (a*(a + 1) / 2 * (2 * a + 1) / 3) % MOD;
    ll y = (b*(b + 1) / 2 * (2 * b + 1) / 3) % MOD;
    return (x*y%MOD);

}

ll calf(ll x, ll y){
    ll temp = 0;
    ll pos = 0;
    for (ll i = 1; i <= x; i = pos + 1){
        pos = min(x / (x / i), y / (y / i));
        temp = (temp + ((s[pos] - s[i - 1]) % MOD + MOD) % MOD*sum((ll) x / i, (ll)y / i) % MOD) % MOD;
    }
    return temp;
}

int main(){
    int T;
    scanf("%d", &T);
    int cas = 1;
    Moblus();
    while (T--){
        int n, m;
        scanf("%d%d", &m, &n);
        if (m>n) swap(m, n);
        ll ans = 0;
        ll pos = 0;
        for (ll d = 1; d <= m; d=pos+1){
            pos = min(m / (m / d), n / (n / d));
            ans = (ans + 1ll * ((ssum[pos] - ssum[d-1]) % MOD*calf(m / d, n / d) % MOD + MOD) % MOD) % MOD;
        }
        printf("Case #%d: %lld\n", cas++, ans);
    }
    //system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值