We all know that any integer number n is divisible by 1 and n. That is why these two numbers are not the actual divisors of any numbers. The function SOD(n) (sum of divisors) is defined as the summation of all the actual divisors of an integer number n. For example,
SOD(24) = 2+3+4+6+8+12 = 35.
The function CSOD(n) (cumulative SOD) of an integer n, is defined as below:
Given the value of n, your job is to find the value of CSOD(n).
Input
Input starts with an integer T (≤ 1000), denoting the number of test cases.
Each case contains an integer n (0 ≤ n ≤ 2 * 109).
Output
For each case, print the case number and the result. You may assume that each output will fit into a 64 bit signed integer.
Sample Input |
Output for Sample Input |
3 2 100 200000000 |
Case 1: 0 Case 2: 3150 Case 3: 12898681201837053 |
题意:求1-n(n<=2e9)之间每个数的SOD之和,定义SOD(x)为x除去1和自己以外的所有因数之和
题解:首先考虑O(n)做法
对于数i,在1-n的因子中出现的次数为(n/i)-1
所产生的贡献是[(n/i)-1]*i
这样得到了O(n)的算法
for (int i=2; i<=n; ++i) { sum+=(n/i-1)*i; }
但是数据的范围是2e9显然是会TLE的
我们考虑优化,如果你做过一些莫比乌斯反演的题目,你会下意识地发现:当i非常大的时候,因子出现次数很久才会变化一次,比如说(n/3+1)~n/2之间可能有几万个数但是他们都只出现了一次,这种情况就可以使用整除分块来优化
这些数是连续的,他们出现的次数又相同,sum=i*time+(i+1)*time+....+(i+k)*time 发现了吗?这就是个等差数列,可以直接用等差数列求和公式搞。但是这个东西直接将次数从出现一次枚举到出现n次复杂度还是O(n)的
怎么办呢?思考一下对于出现1-sqrt(n)次的数字我们进行第二种操作,那么出现sqrt(n)+1次的数字有几个?只有n/[sqrt(n)+1]左右个了,对于这sqrt(n)个数字,我们可以直接用第一种方法暴力搞
总的复杂度是O(sqrt(n))的
代码如下:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,pos,next,lim; long long ans; int main() { int t,ttt=0; scanf("%d",&t); while(t--) { ans=0ll; ttt++; scanf("%d",&n); lim=sqrt(n); pos=n/2; for(int i=2; i<=lim; i++) { next=n/(i+1); ans+=1ll*(pos-next)*(pos+next+1)*(i-1)/2; pos=next; } for(int i=2; i<=pos; i++) { ans+=1ll*i*(n/i-1); } printf("Case %d: %lld ",ttt,ans); } }
这题还是非常高妙的啊~