2154: Crash的数字表格
Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 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
Sample Output
【数据规模和约定】
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 ",(ans%mod+mod)%mod); 37 return 0; 38 }
上面的代码为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 ",(ans+mod)%mod); 43 return 0; 44 }