【2021.6.26 NOI模拟】Problem B. 简单题 another solution
Problem Description
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
首先,我们从小到大枚举 \(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); }
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现