洛谷P2261 [CQOI2007] 余数求和

传送门

原题

题目描述

给出正整数 \(n\)\(k\),请计算

\[G(n, k) = \sum_{i = 1}^n k \bmod i \]

其中 \(k\bmod i\) 表示 \(k\) 除以 \(i\) 的余数。

输入格式

输入只有一行两个整数,分别表示 \(n\)\(k\)

输出格式

输出一行一个整数表示答案。

输入输出样例 #1

输入 #1

10 5

输出 #1

29

说明/提示

样例 1 解释

\(G(10, 5)=0+1+2+1+0+5+5+5+5+5=29\)

数据规模与约定

  • 对于 \(30\%\) 的数据,保证 \(n , k \leq 10^3\)
  • 对于 \(60\%\) 的数据,保证 \(n, k \leq 10^6\)
  • 对于 \(100\%\) 的数据,保证 \(1 \leq n, k \leq 10^9\)

2024/2/13 添加一组 hack 数据

整理&思路

这道题的常规暴力思路就是:

for(int i=1;i<=n;i++)
    sum+=k%i;

但是很明显,这道题没有这么简单。首先就是精度的问题,其次就是时间的问题——这样的O(n)太慢了。
这时候我们就可以利用这样的神秘小定理
首先 \(a \bmod b = a - b * \lfloor \frac{a}{b} \rfloor\),所以我们就可以得到这样的东西:

\[ans = \sum_{i=1}^{n} k - i * \lfloor \frac{k}{i} \rfloor = n * k - \sum_{i-1}^{n} i * \lfloor \frac{k}{i} \rfloor \]

显然\(\lfloor \frac{k}{i} \rfloor\)大约有\(\sqrt{k}\)种取值,所以就可以得到下面的代码,复杂度\(O(\sqrt{k})\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n, k;

signed main() {
	scanf("%lld%lld",&n,&k);
	int sum=0;
	for(int l=1,r,t;l<=n;l=r+1)
		r=(t=k/l) ? min(k/t,n) : n,sum-=t*(r-l+1)*(l+r)>>1;
	printf("%lld",sum+n*k);
	return 0;
}

恭喜AC!# Hello World

posted @ 2025-09-28 16:35  Kibrel  阅读(8)  评论(0)    收藏  举报