【莫比乌斯反演】【数论】[ZBOJ 2693]jzptab

d=1min(n,m)di=1min(n,m)di2μ(i)×Sum(nid,mid)

这个公式的由来详见: http://blog.csdn.net/jeremygjy/article/details/50592588
这道题目就是Crash的数表的强化版本,因为有多组数据那么 O(n) 的复杂度显然不能够胜任本题目那么我们枚举 id=D 来得到
D=1min(n,m)i|D
那么显然此时的 Di=d 了然后把我们需要的数据带入到第一个式子里面去
D=1min(n,m)i|DDii2μ(i)×Sum(nD,mD)
那么可以变形得到
D=1min(n,m)Sum(nD,nD)i|DDii2μ(i)
那么化简后面的 i|DDii2μ(i) 可以得到
i|DiDμ(i)
那么根据前几道题目的做法枚举每一个i然后倍数求前缀和就好了



这里说一下怎么处理前缀和的问题:
首先我们令当前的数字是 p 枚举的素数是i那么就另当前 Ds(D)=i|DiDμ(i) 那么可以发现

  1. 不包含
    如果 ip 中我们选择的 i 不包含i那么可以发现有 Ds(ip)=i×Ds(p) 因为主要是因为由 Ds(p)=i|pipμ(i) 现在变成了 Ds(p)=i|pipiμ(i) 根据分配律展开就行了,那么如果包含 i 呢?

  2. 包含
    如果 ip 中我们选择的 i 包含i那么有由 Ds(p)=i|pipμ(i) 现在变成了 Ds(p)=i|p(ii)(pi)μ(ii) 因为 μ(ii)=μ(i)μ(i)=μ(i) 所以现在的就变成了 Ds(ip)=Ds(p)×(i)2

综上所述 Ds(ip)=Ds(p)×[i(i)2]

但是如果对于一个数中已经含有因数 i (i’|p)那么如果选择的 i 包含i那么 μ 一定是0所以选择的一定不包含 i 那么对于这种数字 Ds(ip)=iDs(p)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;
#define mod 100000009
const int MAXN = 10001000;
bool notprime[MAXN+10];
int prime[MAXN+10], sum[MAXN+10], Max;
long long Ds[MAXN+10];
void Init(){
    Ds[1] = 1;
    int tmp;
    for(int i=2;i<=Max;i++){
        if(!notprime[i]){
            Ds[i] = (i - (1LL * i * i)%mod)%mod;
            prime[++prime[0]] = i;
        }
        for(int j=1;j<=prime[0]&&(tmp=prime[j]*i)<=Max;j++){
            notprime[tmp] = true;
            if(i%prime[j] == 0){
                Ds[tmp] = prime[j] * Ds[i];
                break;
            }
            Ds[tmp] = (Ds[i] * prime[j] - Ds[i] * prime[j] * prime[j] % mod) %mod;
        }
    }
    for(int i=1;i<=Max;i++){
        Ds[i] += Ds[i-1];
        Ds[i] %= mod;
    }
}
long long Sum(long long a, long long b){
    return (a*(a+1)/2%mod) * (b*(b+1)/2%mod)%mod;
}
void solve(int n, int m, long long &ans){
    int last;
    ans = 0;
    for(long long i=1;i<=n;i=last+1){
        last = min(n/(n/i), m/(m/i));
        ans += 1LL * (Ds[last] - Ds[i-1]) * Sum((1LL*n/i) , (1LL*m/i)) % mod;
        ans %= mod;
    }
}
int main(){
    int T, n, m;
    scanf("%d", &T);
    Max = 10000000;
    Init();
    long long ans;
    for(int i=1;i<=T;i++){
        scanf("%d%d", &n, &m);
        if(n > m) swap(n, m);
        solve(n, m, ans);
        printf("%lld\n", (ans%mod+mod)%mod);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值