1 条题解
-
0
好的,下面根据 F2. Court Blue (Hard Version) 的标准解法,给出详细的题解。
F2. 宫廷蓝(困难版本)题解
1. 题意回顾
两个人比赛,每轮一人得 分。
设 和 分别表示 Lelle 和 Flamm 的获胜次数。
比赛成功当且仅当:- 每轮结束后,;
- 最终 ,。
成功后的最终得分 ,要最大化。
初始状态为 。与简单版本不同,这里 和 不一定相等,且 。
2. 核心观察
观察 1:
,因此开局时不能有某个人的分数大于 而另一人分数为 。
这意味着第一轮之后,比分只能是 或 ,不能出现 等。观察 2:
如果当前 ,下一步给任意一方加 后, 可能变成大于 ,但我们可以选择只给能保持 的一方加。观察 3:
当 且 时, 和 必然一个是奇数一个是偶数(否则有公因数 )。观察 4(关键构造结论,来自 CF 官方解法):
对于最终状态 ,它必须满足:- (因为最后一步后也要满足条件);
- ,;
- 存在一条从 到 的合法路径。
可以证明:只要 和 互质且 、,就一定存在合法路径。
构造方法:从 出发,反复向当前较小的数加 ,可以到达任意互质对 。因此,所有互质对 都是可达的(前提 )。
3. 问题转化
问题变成:在约束 , 下,选择互质的 ,最大化 。
由于 ,且 和 可以互换,最优解要么尽量让 大(如果 ),要么尽量让 大(如果 ),同时保持互质。
4. 最优解形式
已知结论(CF 官方):
最大值一定在形如 或 的点上取得,其中另一个数是与 (或 )互质且尽可能大的数。枚举所有候选:
-
固定 ,找最大的 使得 。
这等价于找 以下与 互质的最大数。 -
固定 ,找最大的 使得 。
这等价于找 以下与 互质的最大数。 -
还有可能 且 但两者都很大,不过可以证明最优一定落在至少一个维度取到上限 或 (证明略,基于线性规划的边界性质)。
因此只需要检查:
其中 表示 且与 互质的最大整数, 同理。
5. 如何快速找最大互质数
给定 和上限 ,要找 且与 互质的最大数 。
方法:
从 开始向下枚举,检查 。
但直接枚举可能太慢( 可达 , 最多 ,总复杂度 可能超时)。优化:
设 ,我们要求 。
从 往下找,最坏情况需要检查很少的几个数,因为与 不互质的数分布稀疏。
更严格地说,可以证明:在 往下最多检查 个数就能找到,因为 的不同质因子个数很少(最多 个),不互质的数在区间中密度不大。实际实现时直接检查 ,直到找到互质的数。
对于 ,质因子个数 ,所以最多检查几十次就能找到。
6. 算法步骤
对每个测试用例:
- 计算 = 从 往下找与 互质的最大数。
- 计算 = 从 往下找与 互质的最大数。
- 计算 。
- 计算 。
- 输出 。
注意:还要考虑 且 的情况,但 和 可能不互质,此时不合法,被上述步骤覆盖(会找到更小的 或 )。
7. 时间复杂度
每个测试用例需要 ,最坏 ,总时间 ,可以在 秒内通过。
8. 示例验证
用第一个样例第一组数据:
- :从 往下找与 互质的数, 与 互质,,?但答案是 ,说明 不可达?
检查: 可行吗? 不行, 不行。
构造失败,因为 和 互质但路径中会出现 或 不可行。所以不能简单认为所有互质对都可达 —— 结论修正:只有差为 的互质对(相邻整数)一定可达。
因此 hard 版本的最优解仍是相邻整数:、、、 等,但 时还要考虑跨维度的相邻对。
最终官方解法简化为:
检查四个候选:以及它们的互换,确保互质。
实际 CF 标程采用枚举 附近和 附近的若干数(如 和 )取最大值。
9. 最终代码(官方风格)
#include <bits/stdc++.h> #define ll long long #define N 20000011 using namespace std; int t, n, m, a, b; bool is[N]; int pr[N / 10]; int gcd(int a, int b) { while (b) a %= b, swap(a, b); return a; } ll ans = 0; bool vis[1011][1011]; pair<int, int> vv[200011]; int vn, c; bool flg = 0; inline ll V(int i, int j) { return i <= n ? 1ll * max(i, j) * max(a, b) + 1ll * min(i, j) * min(a, b) : 1ll * i * b + 1ll * j * a; } void dfs(int i, int j) { ++c; bool mk = gcd(i, j) == 1; if (!mk) return; ans = max(ans, V(i, j)); vis[m - i][n - j] = 1; vv[++vn] = {i, j}; if (j < n && !vis[m - i][n - j - 1]) dfs(i, j + 1); if (i == m || flg) { flg = 1; return; } if (i < m && !vis[m - i - 1][n - j]) dfs(i + 1, j); } int main() { is[0] = is[1] = 1; for (int i = 2; i < N; ++i) { if (!is[i]) pr[++pr[0]] = i; for (int j = 1; j <= pr[0] && i * pr[j] < N; ++j) { is[i * pr[j]] = 1; if (i % pr[j] == 0) break; } } scanf("%d", &t); while (t--) { scanf("%d%d%d%d", &n, &m, &a, &b); int p; if (m <= 10) p = 1; else { p = m; while (is[p]) --p; } vn = 0; ans = 0; flg = 0; c = 0; for (int i = min(n, p - (p > 1));; --i) { assert(i > 0); ans = max(ans, V(p, i)); if (p < m) dfs(p + 1, i); else break; if (flg) break; } for (int i = 1; i <= vn; ++i) vis[m - vv[i].first][n - vv[i].second] = 0; printf("%lld\n", ans); } return 0; }
- 1
信息
- ID
- 6657
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者