AcWing 199. 余数之和
考察:推导公式
蓝书是将本题归纳到约数里,我实在没看出来这道题和约数有啥关系
这道题计算k%(1~n)的值,实际上是求k - [k/i]*i的值.转化后将式子累加得到n*k-累加和[k/i]*i.
n*k是已知的式子,i在循环里枚举,唯一要求的是[k/i].一个个求是肯定不行的,因此需要求得某些规律
假设n==10,k==10时,可以发现:
k/i | 10 | 5 | 3 | 2 | 2 | 1 | 1 | 1 | 1 | 1 |
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
当i>10,k/i就==0,因此不再画出
在i=4~5和6~9的时候,我们可以发现k/i是连续的.我们可以将这段通过求和公式累加而避免循环.通过推导发现连续区间的下界在k/[k/i].上界就是下界+1.
然后就可以通过求和公式计算答案
易错:
- 当我们计算连续区间的和时,要将tmp放在前面,这样式子就被转化为long long防止溢出
- 计算下界的时候要防止下界超出n范围外
- 总共有2sqrt(n)个不同的取值,分为分母<=sqrt(a)与>sqrt(a)证明.
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 typedef long long ll; 5 ll n,k,ans; 6 int main() 7 { 8 ll st,ed; 9 scanf("%lld%lld",&n,&k); 10 ans = n*k; 11 for(ll i=1;i<=n;i=ed+1) 12 { 13 int t = k/i; 14 st = k/(t+1)+1,ed = t==0?n:min(k/t,n); 15 ll sum = (st+ed)*(ed-st+1)/2*t; 16 ans-=sum; 17 } 18 printf("%lld\n",ans); 19 return 0; 20 }
2021.1.24 打表感觉更容易发现规律,通过打表发现k/i有连续区间.然后二刷还是踩了几个坑
- 关于ed的取值,ed一定不能超过n,当k>n,k/i==1时,k/t就会超过n
- 不记得会不会爆int,干脆全用了long long