CQOI2007余数之和(BZOJ1257)解题报告
CQOI2007余数之和(BZOJ1257)解题报告
【问题描述】
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数。 例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7
50%的数据满足:1<=n, k<=1000
100%的数据满足:1<=n ,k<=10^9
【分析】
显然朴素枚举肯定TLE。对此式子加以观察。
若k<i 那么 k mod i=k;所以j(n,k)中大于k的一段可以先求出来。
当n<=k 时,我们可以以sqrt(k)为分界点分为两段处理,前半段可以循环一遍直接处理。而后半段也可以在O(sqrt(N))的时间内求解。
为什么呢?现在加以说明。我们知道,k mod i=k-(k div i)*i(这里div表示整除)(sqrt(k)<i<=n<=k)而可能会有多个连续的i使得k div i的值是相等的,设为区间[l,r],那么在这区间中所有k mod i的和可以利用等差数列求出,而对于i>sqrt(k), k div i必然不大于sqrt(k),所以我们可以枚举k div i的值,求出对应的区间,在O(sqrt(k))的时间内出解。
顺便介绍一下怎么求区间。设a=k div i,求i的取值区间。那么对于每个i,必然有i*a<=k,i*(a+1)>k,解得区间[l,r],l=trunc(k/(a+1))+1,r=trunc(k/a).(trunc为截尾,在正数范围内相当于下取整)。
讲得不是很好,详见代码。
【代码】
1 var i,j:longint;
2 n,k,l,r,sq,ans:int64;
3 begin
4 readln(n,k);
5 ans:=0;
6 if n>k then begin ans:=(n-k)*k;n:=k;end;
7 sq:=trunc(sqrt(k)+0.999999999);
8 for i:=1 to sq do
9 inc(ans,k mod i);
10 for i:=1 to sq do
11 begin
12 l:=trunc(k/(i+1))+1;r:=trunc(k/i);
13 if l<=sq then l:=sq+1;
14 if r>n then r:=n;
15 if l>r then continue;
16 inc(ans,{(r-l+1)*k-i*(r-l+1)*(l+r)div 2}(r-l+1)*(2*k-i*l-i*r)div 2);
17 end;
18 writeln(ans);
19 end.
有问题请留言。