【2021.6.26 NOI模拟】Problem B. 简单题 another solution

Problem Description

image.png

Input

从文件 b.in 中读入数据。

一个正整数 n。

Output

输出到文件 b.out 中。

一个整数表示答案。

Sample Data

Input #1 Copy
5
Output #1 Copy
31
Input #2 Copy
50
Output #2 Copy
2885

Data Constraint

image.png

首先,我们从小到大枚举 \(n\),假设当前枚举到 \(i\)\(S\) 会多出两个数 \(2i-1\)\(2i\)。因为要插入其中一个到 \(A\) 中,还要使得和尽量小,所以我们选择向 \(A\) 插入 \(2i-1\)

此时,\(A\) 中可能会出现 \(2i-1\) 的因子(不是质因子),那么我们就枚举出这些因子并把它们从 \(A\) 中移除。

假设我们移除了 \(x\) 个因子,那么 \(A\) 的大小就会减少 \(x\)。所以我们还要再插入 \(x\) 个数。这些数就选作刚刚我们移除的数的倍数。

因为 \(A\) 中不会同时出现倍数,所以我们插入每个被移除数最小的倍数——它们的 \(2\) 倍。

插入了部分数后,我们同样也要担心这些数是否在 \(A\) 中有因子,所以我们重复上面的操作:找因子,从 \(A\) 中移除,将这些移除的数的 \(2\) 倍加入 \(A\) 中。具体 dfs 就可以。因为每个数都只会进行一次这个操作,所以时间复杂度是 \(O(n\times 找因子时间)\) 的。实现较差可以做到 \(O(n\sqrt{n})\),但如果你的实现比较好,比如枚举每个数的倍数加入,可以做到 \(O(n\ln n)\) 的。这个做法不知道比正解差多少倍,但是想起来简单,没有弯路。但是需要注意常数。比如用链式前向星代替 vector

#include <cstdio>
#include <vector>
using namespace std;
#define ll long long
#define N 1000010
ll n, ans, a[N];
ll head[N], nxt[13470035], to[13470035], cnt;
void addEdge(ll u, ll v) {
	cnt ++;
	to[cnt] = v;
	nxt[cnt] = head[u];
	head[u] = cnt;
}
void init() {
	for(ll i = 1; i <= n; i ++) {
		addEdge(i, 1);
		for(ll j = 2; i * j <= 2 * n; j ++) {
			addEdge(i * j, i);
		}
	}
}
void dfs(ll x) {
	for(ll j = head[x]; j; j = nxt[j]) {
		ll i = to[j];
		if(a[i]) {	// 有因数,除掉
			a[i] = 0;
			ans -= i;
			a[i * 2] = 1;
			ans += i * 2;
			dfs(i * 2);
		}
	}
}
int main() {
	freopen("b.in", "r", stdin);
	freopen("b.out", "w", stdout);
	scanf("%lld", &n);
	init();
	a[1] = 1;
	ans = 1;
	for(ll i = 2; i <= n; i ++) {
		ans += 2 * i - 1;	// 必须加入
		a[2 * i - 1] = 1;
		dfs(2 * i - 1);
//		printf("%lld %lld\n", i, ans);
	}
	printf("%lld", ans);
}
posted @ 2024-04-06 15:44  ZnPdCo  阅读(7)  评论(0编辑  收藏  举报