2026 年 ACM 暑假集训
2026 年 ACM 暑假集训
2026 牛客暑期多校训练营 1
F. Permutation Generation
给定 \(n \in \mathbb{N}^*\),记 \(N = \mathbb{N} \cap [0, n)\)。 对于 \(N\) 的一个排列 \(p = (p_i)_{i = 0}^{n - 1}\),记
\[f(p) = \sum_{i = 0}^{n - 1} \sum_{j = i + 1}^{n - 1}(p_j - p_i). \]现在给出 \(N\) 的一个排列 \(p\) 和 \(k,v \in N\),你需要求出 \(N\) 的一个排列 \(q = (q_i)_{i = 0}^{n - 1}\),使得以下条件都成立:
- \(q_k = v\)。
- \(f(p) \equiv f(q) \pmod{n}\)。
或报告无解。
保证 \(n \leq 2 \times 10^5\)。
\(f\) 是容易化简的:
如果再利用上 \(p\) 是排列这一性质的话,还可以进一步化简为
我和一位队友都注意到,如果交换 \(p\) 的第 \(x\) 和第 \(y\) 项得到 \(p'\),会使其函数值变化
因此一直在考虑,要么从 \(p\) 交换出 \(q_k = v\) 后再想办法消除其影响,或直接从顺序交换出 \(q_k = v\) 后再凑出 \(f(q) \equiv f(p) \pmod{n}\)。我甚么办法也没想出来,队友倒是搞出了一种,但没有覆盖到所有情况,导致 WA。
后来我们通过枚举小的数据,观察到都是有解的,因此猜测必定有解,但没有甚么用。
当另一位队友终于写完 J 题大模拟后再看此题时,观察枚举的数据时注意到每个 \(n\) 都有答案可以通过 circular shift(循环移动?)得到,遂出正解。
这里证明一下正确性:设将 \(p\) 循环左移一位得到 \(p'\),有
所以只需通过循环移动使第 \(k\) 位为 \(v\) 即可。时间复杂度 \(O(1)\)。
后来 Gemini 告诉我们,在出现 \(\bmod n\) 时应当优先考虑 circular shift。这是我们没有经验。
inline void solve() {
uint32_t n, idx, val;
std::cin >> n >> idx >> val;
std::vector<uint32_t> perm(n);
uint32_t pos;
for (uint32_t i = 0; i < n; ++i) {
std::cin >> perm[i];
if (perm[i] == val) pos = i;
}
uint32_t lsh = pos - idx + n;
if (lsh >= n) lsh -= n;
for (uint32_t i = lsh; i < n; ++i) {
std::cout << perm[i] << ' ';
}
for (size_t i = 0; i < lsh; ++i) {
std::cout << perm[i] << ' ';
}
std::cout << '\n';
}
G. Precision Error?!
给定 \(n \in \mathbb{N}^*\),需要找出一个点集 \(S \subset \mathbb{R}^3\) 使得
- \(|S| \leq 2 n + 2\)。
- \((\forall P \in S)\ (|\{Q \in S: \Vert\vec{PQ}\Vert_2 = 1\}| = n)\)。
为了允许浮点误差,取精度 \(\epsilon = 0.01\), \(S\) 需要满足
- \((\forall P,Q \in S)\ (P = Q \lor \Vert\vec{PQ}\Vert_2 > \epsilon)\)。
- \(|S| \leq 2 n + 2\)。
- \((\forall P \in S)\ (|\{Q \in S: \Vert\vec{PQ}\Vert_2 \in (1 - \epsilon, 1 + \epsilon)\}| = n)\)。
保证 \(n \leq 100\)。有 \(T \leq 100\) 组测试。
看到题解真气笑了。我就说这个 \(\epsilon\) 怎么这么大,别的题都是 \(10^{-5}\)、\(10^{-6}\),这里来个 \(10^{-2}\),他妈的,果然有蹊跷。
注意到这里的 \(\epsilon\) 相当大,所以我们可以通过构造两部分点,使得同一部分的点之间非常近但不比 \(\epsilon\) 近,而不同部分的点之间的距离都几乎为 \(1\)。
对于平面 \(z = 0\) 上的一个点,在平面 \(z = 1\) 中有一个半径为 \(r = \sqrt{(1 + \epsilon)^2 - 1^2} \approx 0.1418 > 14 \epsilon\) 的圆,这个圆内的点到该点的距离都在 \([1, 1 + \epsilon)\)。这实际上是因为 \(\sqrt{1 + \left(\sqrt{2 \epsilon}\right)^2} = 1 + \epsilon + o(\epsilon^2)\),即圆的半径 \(r \approx \sqrt{2 \epsilon}\)。我们可以在两个平面上各放 \(n\) 个点。为了让两个平面的点互相在对方的容忍范围中,我们需要使它们都位于圆柱 \(x^2 + y^2 \leq \left(\dfrac{1}{2} r\right)^2\) 内(当然,圆心位置实际上是任意的)。
简便起见,考虑将每个平面的点以正方形点阵排布,即用一个 \(\left\lceil \sqrt{n}\right\rceil \times \left\lceil \sqrt{n}\right\rceil\) 点阵排布这 \(n\) 个点。这样的正方形点阵中,设两个点的最近距离(即相邻两点的距离)为 \(d\),则两个点的最远距离为 \(\left(\left\lceil \sqrt{n}\right\rceil - 1\right) \cdot \sqrt{2} d\)。我们希望
解该不等式组得
由 \(n \leq 100\) 得上界
所以完全是可以的。
constexpr double D = 0.011;
constexpr size_t N = 100, S = 10; // sqrt(N)
std::array<std::pair<double, double>, N> points;
inline void preprocess() {
for (size_t i = 0; i < S; ++i) {
for (size_t j = 0; j < S; ++j) {
points[i * S + j] = {D * i, D * j};
}
}
std::cout << std::fixed << std::setprecision(9);
}
inline void solve() {
size_t n;
std::cin >> n;
std::cout << (n << 1) << '\n';
for (int z = 0; z < 2; ++z) {
for (size_t i = 0; i < n; ++i) {
std::cout << points[i].first << ' ' << points[i].second << ' ' << z << '\n';
}
}
}
时间复杂度:\(O(n)\) 预处理,\(O(1)\) 查询。

浙公网安备 33010602011771号