Processing math: 25%

BZOJ_4176_Lucas的数论_杜教筛+莫比乌斯反演

BZOJ_4176_Lucas的数论_杜教筛+莫比乌斯反演

Description

去年的Lucas非常喜欢数论题,但是一年以后的Lucas却不那么喜欢了。

在整理以前的试题时,发现了这样一道题目“求Sigma(f(i)),其中1<=i<=N”,其中 表示i的约数个数。他现在长大了,题目也变难了。
求如下表达式的值:
 
其中 表示ij的约数个数。
他发现答案有点大,只需要输出模1000000007的值。

Input

第一行一个整数n。

Output

 一行一个整数ans,表示答案模1000000007的值。

Sample Input

2

Sample Output

8

HINT

 对于100%的数据n <= 10^9。


 

f(nm)=i|nj|m[gcd(i,j)=1]

证明:首先ij|nm,但直接枚举ij会有些重复。

gcd(i,j)=k,a=i/k,b=j/k

发现一定能枚举到i=ak,j=b,i,此时gcd(i',j')=gcd(i'',j'')=1

考虑a*b*k这个约数其实是被枚举了两次,不妨用这两次中的一个来‘代表’a*b*k*k

因此我们枚举gcd(i,j)=1i,j即可,只是此时ij可能有相等的,他们代表的约数不同。

可以举n=2,m=6的例子自己手算一下。

\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}f(ij) = \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum \limits_{x|i}\sum\limits_{y|j}[gcd(x,y)=1]

=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum \limits_{x|i}\sum\limits_{y|j}\sum\limits_{d|gcd(x,y)}\mu(d)

=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum \limits_{x|i}\sum\limits_{y|j}\sum\limits_{d|gcd(x,y)}\mu(d)

=\sum\limits_{d=1}^{n}\mu(d)\sum\limits_{i=1}^{n/d}\sum\limits_{j=1}^{n/d}\sum \limits_{x=1}^{\frac{n/d}{i}}\sum\limits_{y=1}^{\frac{n/d}{j}}

=\sum\limits_{d=1}^{n}\mu(d)(\sum\limits_{i=1}^{n/d}\frac{n/d}{i})^{2}

\mu的前缀和用杜教筛搞,后面的只有\sqrt{n/d}种取值。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
ll mod=1000000007;
map<ll,ll>f;
int m=1000000;
int prime[1000050],cnt,miu[1000050],summiu[1000050];
bool vis[1000050];
ll calc1(ll n) {
    if(n<=m) return summiu[n];
    if(f.count(n)) return f[n];
    ll i,lst,ans=1;
    for(i=2;i<=n;i=lst+1) {
        lst=n/(n/i);
        ans=(ans-(lst-i+1)*calc1(n/i)%mod+mod)%mod;
    }
    return f[n]=ans;
}
ll calc2(ll n)
{
    ll ans=0,i,lst;
    for(i=1;i<=n;i=lst+1) {
        lst=n/(n/i);
        ans=(ans+n/i*(lst-i+1))%mod;
    }
    return ans*ans%mod;
}
void init() {
    int i,j;
    miu[1]=summiu[1]=1;
    for(i=2;i<=m;i++) {
        if(!vis[i]) {
            prime[++cnt]=i;
            miu[i]=-1;
        }
        for(j=1;j<=cnt&&i*prime[j]<=m;j++) {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0) {
                miu[i*prime[j]]=0;
                break;
            }
            miu[i*prime[j]]=-miu[i];
        }
        summiu[i]=summiu[i-1]+miu[i];
    }
}
int main() {
    init();
    ll n,ans=0,i,lst;
    scanf("%lld",&n);
    for(i=1;i<=n;i=lst+1) {
        lst=n/(n/i);
        ans=(ans+(calc1(lst)-calc1(i-1)+mod)%mod*calc2(n/i))%mod;
    }
    printf("%lld\n",ans);
}

 

posted @   fcwww  阅读(224)  评论(0)    收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示