「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;
}