[CQOI2007]余数求和

[CQOI2007]余数求和

题目链接

题意简述:

  给出n,k求k%1~n每一个数的和

解题思路

  1.式子变形

  首先将原始式子变形成为nk-(k/1*1+k/2*2...k/n*n),此处的除法都是向下取整,也就是说不可以使用运算律了。

  2.区间思想

  那么我们通过简化式子,得到了一个不能用运算律的式子。此时我们发现不能暴力去算他。于是尝试着找了一会规律,可得到一个表格。(以100为例)

[CQOI2007]余数求和_第1张图片

 

 (相同的用颜色标记出来)

如果说颜色是区间的话。那么知道左端点就可以知道右端点

记左端点为x,右端点为y.   y=k/(k/i),注意此处k/i向下取整。

又由于区间与区间是相连的,所以说下一个区间的左端点为这一个区间的右端点加一

并且第一个区间的左端点为1.由此可以计算出所有的区间。

3.利用区间思想解题&额外补充

  1)解题:

      将区间思想与式子结合起来。将k/1*1+k/2*2...k/n*n按照区间分开来计算。知道k/i的定值,提取一个k/i的公因数,剩下的x,x+1,x+2...求和。

  2)额外补充:

       右端点取出来可能大于n,要处理一下。

       当n>k的时候,会出现k/i=0的情况,先把后面的累积到答案中,然后切换成n=k的子任务。

代码

#include
using namespace std;
#define ll long long
ll n,k,ans;
int main(){
    cin>>n>>k;
    if(n>k){
        ans=(n-k)*k;
        n=k;
    }
    ll z=1,y=1;
    while(z<=n){
        int x=k/z,num;
        y=min(n,k/x);
        num=y-z+1;
        ans+=num*k-x*((z+y)*num/2);
        z=y+1;
    }
    cout<<ans;
    return 0;
}
View Code

你可能感兴趣的:([CQOI2007]余数求和)