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; }