「Stoi2031」枫

题目大意

给定 \(n,k\),对 \(1,2,\dots,n\) 一直操作,每次操作交替从小到大或从大到小的顺序取走当前的第 \((k+1)x +1\) 个数(\(x\in \mathbb{N}^+\)\((k+1)x +1\) 不超过剩余数总数),求最后一个取走的数的编号。多次询问。

思路

\(f_n\) 为有 \(n\) 个数时的答案。

假设我们现在有 \(n\) 个数,第一片枫叶我们暂且不看,剩下的枫叶我们是每 \(k+1\) 个拿 \(1\) 个,所以第一轮总共拿了 \(\frac{n-1}{k+1}+1\) 个,那么剩下的就是 \(last=n - 1 - \frac{n-1}{k+1}\)

设现在的局面是挽回了第一次之后,倒着去挽回的情况,相当于剩下的序列进行了反转。

设当前局面的思念位置为 \(x\),那么反转之前的局面时,这个思念的位置为 \(pos = last - x + 1\)

它和之前的状态的位置差的是在 \(pos\) 之前拿走的枫叶数量,又因为我们在 \(pos\) 之前拿走的枫叶数量为 \(1+\frac{pos-1}{k}\),那么这个思念在当前局面的位置为 \(pos + 1+\frac{pos-1}{k}\)

这样我们可以递推预处理,然后查询。

Code

#include <bits/stdc++.h>

using namespace std;

int T,n,q,m,k;

int ans[1005000];

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    cin >> T;

    for(int t = 1;t <= T; t++) {
        cin >> q >> m;
        k = t;

        ans[1] = 1;

        int last,pos;
        for(int i = 2;i <= m; i++) {
            last = i - (i - 1) / (k + 1) - 1;
            pos = last - ans[last] + 1;
            ans[i] = pos + (pos - 1) / k + 1;
        }

        for(int i = 1;i <= q; i++) {
            cin >> n;
            std::cout << ans[n] << " ";
        }

        std::cout << "\n";
    }

    return 0;
}
posted @ 2023-08-27 07:52  -白简-  阅读(34)  评论(0编辑  收藏  举报