P2261 [CQOI2007]余数求和

传送门

O(n) 算法可以60分,不用讲了吧...

考虑怎么加快速度

首先在不用“%”的情况下求余数应该懂吧

a%b = a - int(a/b) * b

int() 是向下取整的意思

那么题目要求 k%1 + k%2 + ... + k%n

就等于 k-int(k/1)*1 + k-int(k/2)*2 + ... + k-int(k/n)*n

整理一下

= k*n - (int(k/1)*1 + int(k/2)*2 + ... + int(k/n)*n)

然后我们发现 有一些 int(k/i) 是相等的

所以可以和在一起计算

不同的 int(k/i) 大概有 sqrt(k) 种

讲一下怎么找 i:

(以下除后都向下取整)

显然如果 k%i = 0 ,那么 k/(i+1) 肯定不等于 k/i;如果 k%(i-1)=0,那么 k/i 也肯定不等于 k/(i-1)

所以一个区间的左端点就是 k%i = 0 时的 i+1 ,右端点就是下一个 k%i'=0 的 i'

考虑怎么求 i' ,显然如果k%i=0,那么 i 就是 k 的一个因子,可以O(sqrt(k)) 找因子;

还有一种比较好的方法:如果 k%i' = 0 那么 k = q*i' (q为正整数),所以 q = k/i'

因为k/i' = int(k/ (i+1) ),所以 q=int(k/ (i+1) ),所以 i' = k/q = k/int(k/ (i+1) )

 

具体实现还是看代码吧

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
long long n,m,ans;
int main()
{
    cin>>n>>m;
    ans=n*m;
    for(long long l=1,r;l<=n;l=r+1)
    //l,r表示对于任意 l<=i<=r,int(k/i)都相等
    {
        if(m/l) r=min(m/(m/l),n);
        else r=n;
        //求出r的大小
        ans-=(m/l)* (r-l+1)*(r+l)/2;
        //(r-l+1)*(r-l)/2 就等于 l+ (l+1) + ... + r 的和
    }
    cout<<ans;
    return 0;
}    

 

posted @ 2018-09-07 16:20  LLTYYC  阅读(172)  评论(0编辑  收藏  举报