LightOJ-1098 A New Function (数论、思维)

题目链接
其实是道简单题,但是感觉这里思维挺有意思的所以写个题解

题目大意:求出\(CSOD(n)=\sum_{i=1}^nSOD(i)\) 其中\(SOD(i)\)代表\(i\)的所有非1和它本身的约数和

首先确定约数\(x\)范围是从\(2\sim n/2\),然后对于每个约数\(x\)来说,从\(1\sim n\)的,以\(x\)作为因子的数有\([n/x]\)个,也就是\(x,2x,3x,4x...px(px\le n)\)除去第一个\(\textbf{x}\)(由于要求为约数和,不能包括自身)这里的和就是\(([n/x]-1)*x\),这里就有了一种复杂度为\(O(n)\)的朴素做法,显然会无情TLE

于是可以发现,对于每个约数\(x\)来说,\(2x,3x,4x...px(px\le n)\)中所对应的\(2,3,4...p\) 恰巧也是我们所需要的约数。其次\(\sqrt{n}\)\(n\)以内最大所需约数(其余约数均可以用\(n/i\)表示),因此可以将答案表示为\(([n/x]-1)*x+\underline{(k+1)+(k+2)+...+p}\),后半部分显然直接等差数列求和,这样复杂度就降为\(O(\sqrt{n})\)

注意:由于 \(2\sim \sqrt{n}\)的约数都被遍历过,因此要在等差数列中删去

代码如下:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;

int main()
{
	int T, n;
	scanf("%d", &T);
	for (int cas = 1; cas <= T; ++cas)
	{
		LL result = 0;
		scanf("%d", &n);
		int end = sqrt(n);
		for (int i = 2; i <= end; ++i)
		{
			LL cnt = n / i - 1;
			result += cnt * i;
			++cnt;
			if (cnt > end)
				result += ((LL)end + 1 + cnt) * (cnt - end) / 2;
		}
		printf("Case %d: %lld\n", cas, result);
	}
}
posted @ 2020-11-07 11:19  DreamW1ngs  阅读(98)  评论(0)    收藏  举报