[Luogu P2261] [CQOI2007]余数求和 (取模计算)

题面

传送门:https://www.luogu.org/problemnew/show/P2261
[Luogu P2261] [CQOI2007]余数求和 (取模计算)

[Luogu P2261] [CQOI2007]余数求和 (取模计算)


Solution

这题显然有一个O(n)的直接计算法,60分到手。

接下来我们就可以拿出草稿纸推一推式子了

首先,取模运算在这里很不和谐,我们得转换一下。

对于任意取模计算,我们都有:

[Luogu P2261] [CQOI2007]余数求和 (取模计算)

所以,我们可以做以下推算

[Luogu P2261] [CQOI2007]余数求和 (取模计算)

经过一些手算,我们发现k/i(向下取整)是由一段一段的区间组成的,如下图

[Luogu P2261] [CQOI2007]余数求和 (取模计算)

显然,每段区间的右端点可以通过二分的方法来找

对于每一段区间,我们可以把k/i提出来,括号里面就变成了(i+(i+1)+(i+2)+(i+3)+.....+右端点) 直接用等差数列来算就好

时间复杂度我不会算XD  


Code

//Luogu P2261 [CQOI2007]余数求和
//Jul,7th
//取模运算推一推
#include<iostream>
#include<cstdio>
using namespace std;
int main(int argc, char **argv)
{
//freopen("sum.in","r",stdin);
//freopen("sum.out","w",stdout);
long long n,K;
scanf("%lld%lld",&n,&K); long long ans=n*K;
for(long long i=1;i<=n;i++)
{
long long temp=K/i;
long long l=i,r=n,mid,nxt=i;
while(l<=r)
{
mid=(l+r)/2;
if(K/mid==temp)
nxt=max(nxt,mid),l=mid+1;
else
r=mid-1;
}
ans-=(((i+nxt)*(nxt-i+1))/2)*temp;
i=nxt;
} printf("%lld",ans);
return 0;
}

正解(C++)

上一篇:洛谷 P2261 [CQOI2007]余数求和 ||整除(数论)分块


下一篇:js画力导图