【莫比乌斯反演】【数论】[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;
}

posted on 2016-01-27 16:59  JeremyGuo  阅读(144)  评论(0编辑  收藏  举报

导航