洛谷P2261 [CQOI2007]余数求和

题目描述

给出正整数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 }
View Code

 

posted @ 2017-10-14 22:46  秋千旁的蜂蝶~  阅读(204)  评论(0编辑  收藏  举报