P2261余数求和
Posted ukcxrtjr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)来解决了.
再讨论一下比较难想的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;
以上是关于P2261余数求和的主要内容,如果未能解决你的问题,请参考以下文章