P7485 「Stoi2031」枫 题解
题意
给定两个正整数 \(n, k\),将 \(n\) 个元素排成一个序列并按 \(1 \rightarrow n\) 编号,每次轮流从前 / 后开始每隔 \(k\) 个未删除元素删除一个元素,求最终剩余的一个元素的编号。
题解
因为 \(k\) 的取值很少,考虑对于每个 \(k\) 进行递推。
在已钦定 \(k\) 取值的情况下,设 \(f_n\) 为对应的答案。
首先对于初始值,有 \(f_1 = 1\)。对于 \(n > 1\),考虑有 \(k \ge 1\),所以执行完第一轮删除之后一定有剩余元素,并且删除完成后元素个数一定会减少,所以可以利用小序列的答案更新出大序列的答案。设 \(x = n - \left\lceil\dfrac{n}{k + 1}\right\rceil\) 即长度为 \(n\) 的序列经过第一轮删除之后剩余的元素数量。设 \(b = x + 1 - f_x\),即最终答案是经过第一轮删除后的第几个元素,那么有转移
\[f_n = b + \left\lceil\dfrac{b}{k}\right\rceil
\]
后面加上的数是补上在删除后第 \(b\) 个元素前被删除的元素数量。
总复杂度 \(\mathcal{O}(nk)\),可以通过本题。
Code
#include <bits/stdc++.h>
typedef int valueType;
typedef std::vector<valueType> ValueVector;
constexpr valueType V = 1e6 + 5;
std::array<valueType, V> F;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType T;
std::cin >> T;
std::stringstream str;
for (valueType k = 1; k <= T; ++k) {
valueType Q, M;
std::cin >> Q >> M;
F[1] = 1;
for (valueType i = 2; i <= M; ++i) {
valueType const from = i - (i + k) / (k + 1);
valueType const x = from + 1 - F[from];
F[i] = x + (x + k - 1) / (k);
}
for (valueType i = 0; i < Q; ++i) {
valueType x;
std::cin >> x;
str << F[x] << ' ';
}
str << '\n';
}
std::cout << str.str() << std::flush;
return 0;
}