题意:
首先一号位置上必为 1,剩下的 n - 1 张牌转化为约瑟夫环问题,共有 n - 1 个人,每数 m + 1 个数淘汰一人,共有 q 次询问,每次询问求第 k 个人是第几个被淘汰的。(n, k <= 40000000, m <= 10, q <= 100)
链接:
https://nanti.jisuanke.com/t/41352
题解:
首先约瑟夫环有线性 O(n) 做法,但是考虑到可能过不了,注意到 m 很小,这种情况下一圈下去可以淘汰 n / m 个人,规模可以小得多,原理还是跟线性的一样,但复杂度可证明为 O(mlogn)。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e6 + 5;
const int maxm = 2e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n, m, q;
int dfs(int n, int k){
if(n == 1) return 1;
if(n < m){
if(k == (m - 1) % n) return 1;
int np = m % n;
k = (n - np + k - (k >= np)) % (n - 1);
return dfs(n - 1, k) + 1;
}
if((k + 1) % m == 0) return (k + 1) / m;
int np = n - n % m;
k = (n - np + k - k / m) % (n - 1);
return dfs(n - n / m, k) + n / m;
}
int main(){
int t; scanf("%d", &t);
while(t--){
scanf("%d%d%d", &n, &m, &q); --n, ++m;
while(q--){
int k; scanf("%d", &k);
if(k == 1) { printf("1\n"); continue; }
int ret = dfs(n, k - 2) + 1;
printf("%d\n", ret);
}
}
}