[BZOJ 2154] Crash的数字表格

2154: Crash的数字表格

Time Limit: 20 Sec  Memory Limit: 259 MB
Submit: 1337  Solved: 531
[Submit][Status][Discuss]

Description

今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时被a和b整除的最小正整数。例如,LCM(6, 8) = 24。回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下: 1 2 3 4 5 2 2 6 4 10 3 6 3 12 15 4 4 12 4 20 看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod 20101009的值。

Input

输入的第一行包含两个正整数,分别表示N和M。

Output

输出一个正整数,表示表格中所有数的和mod 20101009的值。

Sample Input

4 5

Sample Output

122
【数据规模和约定】
100%的数据满足N, M ≤ 107。

HINT

Source

数论

【题解】

用莫比乌斯繁衍(题解均省略取模运算)

设sum2(i)=i*(i+1)/2

设函数sum(i,j)=sum2(i)*sum2(j)

那么,ans=sigma (D=1 to min(n,m)) sum(n/d,m/d)* (sigma (i|D) Di*mu[i])

(这是可以推出来的)

然后呢,YSY聚聚告诉我了一个方法:

线性筛筛素数,记录i最小素数因子为mz[i]

然后,设f[D] 为 (sigma (i|D) Di*mu[i]),那么,

f[D]=f[D/mz[i]](mz[i/mz[i]]==mz[i])

f[D]=f[D/mz[i]]*(1-mz[i]) (else)

那么,即可在O(n)时间内处理

可以用前缀和维护f,则在O(sqrt n)之内处理完

需要强调的是!!!!!!!

括号啊括号!!long long啊long long!!!

WA了10几次,精度爆炸了

首先是sum,不能连乘3次!!

long long 强制转换要放内部!

唉,ysy一遍A,我WA了10几次,TAT

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int prime[1000010];
 4 int tot,mz[10000010];
 5 int f[10000010],n,m;
 6 long long ans;
 7 const int mod=20101009;
 8 long long sum(int ki,int kj) {
 9     return ((((long long)ki*(ki+1)/2)%mod)*(((long long)kj*(kj+1)/2)%mod))%mod;
10 }
11 int main() {
12     //freopen("a.out","w",stdout);
13     scanf("%d%d",&n,&m);
14     if (n>m) {
15         int t=n;
16         n=m;
17         m=t;
18     }
19     for (int i=2;i<=n;++i) {
20         if (!mz[i]) {
21             prime[++tot]=i;
22             mz[i]=i;
23         }
24         for (int j=1;j<=tot&&i*prime[j]<=n;++j) {
25             mz[i*prime[j]]=prime[j];
26             if(i%prime[j]==0) break;
27         }
28     }
29     //for (int i=1;i<=tot;++i) cout<<prime[i]<<endl;
30     //for (int i=1;i<=n;++i) cout<<mz[i]<<endl;
31     f[1]=1;
32     for (int i=2;i<=n;++i)
33         if(mz[i/mz[i]]==mz[i]) f[i]=f[i/mz[i]]%mod;
34         else f[i]=((long long)(f[i/mz[i]]*(1-mz[i])))%mod;
35     for (int i=1;i<=n;++i) ans=(ans+(sum(n/i,m/i)*(((long long)f[i]*i)%mod)))%mod;
36     printf("%lld\n",(ans%mod+mod)%mod);
37     return 0;
38 }
View Code

上面的代码为O(n)算法,8988ms

下面我们可以用前缀和来维护,O(sqrt n) 4989ms

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int prime[1000010];
 4 int tot,mz[10000010];
 5 int f[10000010],n,m;
 6 long long ans;
 7 const int mod=20101009;
 8 long long sum(int ki,int kj) {
 9     return ((((long long)ki*(ki+1)/2)%mod)*(((long long)kj*(kj+1)/2)%mod))%mod;
10 }
11 int main() {
12     //freopen("a.out","w",stdout);
13     scanf("%d%d",&n,&m);
14     if (n>m) {
15         int t=n;
16         n=m;
17         m=t;
18     }
19     for (int i=2;i<=n;++i) {
20         if (!mz[i]) {
21             prime[++tot]=i;
22             mz[i]=i;
23         }
24         for (int j=1;j<=tot&&i*prime[j]<=n;++j) {
25             mz[i*prime[j]]=prime[j];
26             if(i%prime[j]==0) break;
27         }
28     }
29     //for (int i=1;i<=tot;++i) cout<<prime[i]<<endl;
30     //for (int i=1;i<=n;++i) cout<<mz[i]<<endl;
31     f[1]=1;
32     for (int i=2;i<=n;++i)
33         if(mz[i/mz[i]]==mz[i]) f[i]=f[i/mz[i]];
34         else f[i]=((long long)f[i/mz[i]]*(1-mz[i]))%mod;
35     for (int i=2;i<=n;++i)
36         f[i]=((long long)f[i]*i+f[i-1])%mod;
37     int j=0;
38     for (int i=1;i<=n;i=j+1) {
39         j=min(n/(n/i),m/(m/i));
40         ans=(ans+(sum(n/i,m/i)*(long long)(f[j]-f[i-1])%mod))%mod;
41     }
42     printf("%lld\n",(ans+mod)%mod);
43     return 0;
44 }
View Code

 

 

posted @ 2015-06-18 20:48  TonyFang  阅读(188)  评论(0编辑  收藏  举报