P2714 循环不变式的基础应用
你听说过循环不变式吗?不妨来品鉴一下吧:WeLikeStudying 大佬的博客:循环不变式
而这篇文章,只是对大佬博客的小小注解,外加一点实际应用。
-
我们可以把循环不变式理解成一组条件。在每次循环中,我们保证这组条件均为真。而最后循环就可以得出我们想要的结果。
-
如果思考本质,这实际上可以一种数学归纳法。我们保证了循环任何时候都满足条件,只需要验证满足了这组条件就能求出答案。
-
满足的条件可以是显示在代码中的,也可以是隐藏起来的。
举个例子,斐波拉契数列中,我们创造两个变量 ,且保证:
那么就可以求出答案。
- 我们可以发现,选择一组好实现的条件,是化繁为简的关键。
现在我们看到这道题:
-
首先这道题和 有关,再看到数据范围,这意味着我们的复杂度只要和每个数的倍数有关,就能通过此题。
-
最简单的条件是创造数组 ,且 。其中 就是 的答案。但是我们发现这不好实现。
-
考虑扩大条件。我们不能求出恰好的答案,但是我们可以求出至少的答案。具体地,我们定义 为 的个数。我们枚举每个数的倍数,可以在 的时间内求出来。
-
最后,我们寻找这两者的关系。要得到恰好为 的答案,就要把所有比 大的答案都从 剔除出去。因此,我们得出 。
-
注意这个式子去除了一次 。但是由于此时 没有求出,应该为 ,所以没有影响。
总结一下,在程序中,我们只要满足下面的条件,那么 就是答案(设 在序列中出现了 次)。
下面是代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e4 + 5;
int n;
int a[N];
long long ton[N], f[N], g[N];
void solve() {
for (int i = 1; i <= n; ++i) cin >> a[i];
int mxa = 0;
for (int i = 1; i <= n; ++i) mxa = max(mxa, a[i]);
for (int i = 1; i <= mxa; ++i) f[i] = g[i] = ton[i] = 0;
for (int i = 1; i <= n; ++i) ++ton[a[i]];
for (int i = mxa; i >= 1; --i) {
long long sum_g = 0, sum_f = 0;
for (int j = 1; i * j <= mxa; ++j) {
sum_g += ton[i * j];
sum_f += f[i * j];
}
g[i] = (sum_g * (sum_g - 1) * (sum_g - 2) * (sum_g - 3)) / (4 * 3 * 2 * 1);
f[i] = max(1LL * 0, g[i] - sum_f);
}
cout << f[1] << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
while (cin >> n) solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异