Problem:给定,求 。
标签:高中数学题(一元一次不等式+等差数列求和)
首先我们考虑:当时,。对于这部分单独处理即可。
现在我们考虑的情况(时显然答案是0)
对于被除数和除数,。我们现在考虑的就是如何快速累加。
显然,假定不随的变化而变化时,每,。反之,每,。
所以,在某个特定的范围内(前提是不变)变换时,在这些取值下的出可以通过求等差数列(公差为)的方法求出。
举个例子:
当时:当除数(模数)取,商均为1,余数就是,是一个公差为1的等差数列。
问题来了:如何求出上面说的的“特定范围”?
让我们先看之前那个式子:
假设是确定的,我们根据的定义,可知:
又由,得:
现在我们再确定的上界:当以上式子中取时,有:
在式子中,
也就是说:我们可以通过枚举,从而求出对应模数的范围,在此时求出来的模数的范围内,余数的变化规律呈等差数列分布。这样我们便可以利用等差数列通项公式来求了。
Code:
#include<cstdio>
#include<iostream>
using namespace std;
long long n,k,xj,fst,lst,tot,minn,ans;
int main()
{
scanf("%lld%lld",&k,&n);
if(n<=k)
{
ans=(k-n)*n;
k=n-1;
}
xj=n/k;
for(long long a=xj;a<=n;a++)
{
minn=(n+1)/(a+1);
if(k<=100000) break;
//当k取到10万左右时,x的值域涵盖的整数就很少了,这时直接暴力累加答案即可。
if((n+1)%(a+1)>0) minn+=1;
fst=n%k,lst=n%minn;
tot=(lst-fst)/a+1;
ans+=tot*fst+a*tot*(tot-1)/2;
k=minn-1;
}
for(long long i=1;i<=k;i++) ans+=n%i;
cout<<ans;
return 0;
}