P2261余数求和

题面:https://www.luogu.org/problemnew/show/P2261

首先,我们处理一下n>=k的情况,因为这种情况很好处理:

G(n,k)=k%1+k%2+......+k%n,那么,当n>=k时,n>=k的部分k%i就是k,然后就是n<k了

这里,我们定义[c]为对c下取整.

我们不难发现在[k/2]+1到k这个区间里,k%i是一个等差数列,公差为1

在[k/3]+1到[k/2]这个区间里,k%i也是一个等差数列,公差为2

在[k/4]+1到[k/3]这个区间里,k%i也是一个等差数列,公差为3

.........

一直到[k/√k]+1到[k/(√k-1)]这个区间里,k%i也是一个等差数列,公差为(√k-1).

剩下的1到[k/√k]的区间直接暴力求解即可.

这样,我们就能用O(√k)来解决了.

扫描二维码关注公众号,回复: 6790175 查看本文章

再讨论一下比较难想的n<k的情况,

当n<k时,自然就不存在能直接算出k%i的部分了,

有人会问了,这不是更简单一些吗?直接不要超出的部分,再用上面给出的计算不就行了,

是的,大部分是这样,但是,我们需要考虑的是,有可能以上给出的区间中,n并不包含其中,也就是说没必要算那些不包含n的区间了.

那么,我们需要找一个区间,使n包含其中,剩下的都按上面的方法做即可.

Code:

#include<iostream>
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<ctime> using namespace std;
long long n,i,k,l,ans;//注意:整型默认为下取整 int main(){ cin>>n>>k; if(n>=k){//n>=k的部分 ans=(n-k)*1ll*k;//k%i的结果都是k,共有(n-k)个 } else{//否则n<k for(i=1;i*i<=k&&i<n;i++){//找一个区间使得n在其中,i就是区间上界的分母(详情看解释) if(k/i<n){//找到了 l=k/i;//记下来 break;//退出 } l=i;//否则就是当前的i } l++;//这里l++是为了把分母移到区间的下界(详情看解释) ans=1ll*(n-l+1)*(k%n+k/l*(n-l)+k%n)/2;//注意:这里是特殊处理含n的区间(其中上界是n,下界是l,公差为k/l) //高斯速算公式:(首项+末项)*项数/2 //(n-l+1)相当于项数,也就是区间长度 //k%n是首项 //k/l*(n-l)+k%n是末项,因为公差是k/l,中间有(n-l)个数,首项是k%n } for(i=k/n+1;i*i<=k;i++){//两种情况的交集,也就是都需要枚举,一开始i=k/n+1,i<=√k, //这里我们需要一个证明:为什么一开始i=k/n+1,当n>=k时,k/n+1刚好是1,也就是解释里第一个区间的上界分母 //当n<k时,因为刚刚已经处理过了包含n的区间,所以不必再处理,k/n+1刚好是包含n区间的下一个区间的上界分母 ans+=1ll*(k%i+i*(k/i-k/(i+1)-1)+k%i)*(k/i-k/(i+1))/2; //高斯速算公式:(首项+末项)*项数/2 //这里首项是k%i //末项是i*(k/i-k/(i+1)-1)+k%i,因为公差是i,其中一共有k/i-k/(i+1)-1个数,也就是项数-1(详情看解释) //项数是k/i-k/(i+1) } n=k/i;//1到[k/√k]的区间暴力求解 for(i=1;i<=n;i++){ ans+=k%i;//每次ans+k%i } cout<<ans<<endl; return 0; }

猜你喜欢

转载自www.cnblogs.com/ukcxrtjr/p/11163287.html