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;
}
posted @ 2023-08-25 16:09  User-Unauthorized  阅读(36)  评论(0编辑  收藏  举报