题目描述
给出正整数n和k,计算G(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数。例如G(10, 5)=5 mod 1 + 5 mod 2 + 5 mod 3 + 5 mod 4 + 5 mod 5 …… + 5 mod 10=0+1+2+1+0+5+5+5+5+5=29
输入输出格式
输入格式:
两个整数n k
输出格式:
答案
输入输出样例
输入样例#1:
10 5
输出样例#1:
29
说明
30%: n,k <= 1000
60%: n,k <= 10^6
100% n,k <= 10^9
题解:
这差不多是道数学题吧
看样例发现对与n大于k的的部分,k模它的余数一定为k
那么只需考虑k以内的了
k%x可以写成k=a*x+r,模出来的值其实就是r
我想,当x与k很接近时,当x增大时a基本上变化很小。在确定a不变的情况下r随x增加而发生的变化是很好计算的,就是个等差数列嘛!
而显然若a能基本不变那么a一定很小,其小于大的分界线便是sqrt(n)
如果不太理解可以自己试试数算一下,比如48、49之类的
对于a>sqrt(n),也就是x<sqrt(n)时,可以枚举x求余数和
之后便从sqrt(n)到1枚举a,由于在a不变时余数就是个等差数列,那么知道数列的个数与首项、公差就可以求和了。公差由k=a*x+r可知就是a
之后就可以了,这题的细节有点多,需要注意一下
总结一下思路:
发现只需考虑k以内的 -> 由余数想到除,由除想到反比例函数增长快慢sqrt(n)可做分界线 -> 分类讨论发现余数的规律 -> 注意细节进行计算
代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 7 typedef long long ll; 8 int n,k; 9 ll ans=0; 10 11 int main() 12 { 13 int i,m,w,x,y,last; 14 scanf("%d%d",&n,&k); 15 if(n>k) ans+=(ll)(n-k)*k; 16 17 m=sqrt(k);y=k/m; 18 for(i=1;i<=y && i<=n;i++) 19 ans+=k%i; 20 last=y; 21 for(i=m-1;i>0;i--){ 22 w=k/i; 23 x=k%(last+1); 24 if(w<n) ans+=(ll)(w-last)*(x+x-(w-last-1)*i)/2; 25 else{ 26 ans+=(ll)(n-last)*(x+x-(n-last-1)*i)/2; 27 break; 28 } 29 last=w; 30 } 31 printf("%lld",ans); 32 33 return 0; 34 }