题目链接
约瑟夫环算法参考
按上述回答中的方式进行编号,如果是
n
n
n个人围成一圈,每
k
k
k个人踢掉1个,那么第
m
m
m个被踢掉的人编号为
m
k
mk
mk。
n
=
10
,
k
=
3
n=10, k=3
n=10,k=3的情况如图:
可知编号
m
k
+
d
mk+d
mk+d的人下一次编号为
n
+
m
(
k
−
1
)
+
d
(
1
≤
d
<
k
)
n+m(k-1)+d (1\le d<k)
n+m(k−1)+d(1≤d<k)。
若
N
=
n
+
m
(
k
−
1
)
+
d
N=n+m(k-1)+d
N=n+m(k−1)+d,则
m
=
[
N
−
n
−
1
k
−
1
]
m=[\frac{N-n-1}{k-1}]
m=[k−1N−n−1],上一次编号
N
′
=
m
k
+
d
=
N
−
n
+
[
N
−
n
−
1
k
−
1
]
N'=mk+d=N-n+[\frac{N-n-1}{k-1}]
N′=mk+d=N−n+[k−1N−n−1]。
记
k
n
+
1
−
N
=
D
kn+1-N=D
kn+1−N=D,则
D
′
=
⌈
D
k
k
−
1
⌉
D'=\lceil\frac{Dk}{k-1}\rceil
D′=⌈k−1Dk⌉,因此可以
O
(
log
n
)
O(\log n)
O(logn)求解了。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll f(ll n, ll m, ll k){ // n-people, m-th, k-cycle
__int128 D = (__int128)k * (n - m) + 1;
while((__int128)k * n + 1 - D > n){
D = (D * k + k - 2) / (k - 1);
}
return (__int128)k * n + 1 - D;
}
int main(){
int T, cas = 0;
scanf("%d", &T);
ll n, m, k;
while(T--){
scanf("%lld%lld%lld", &n, &m, &k);
printf("Case #%d: %lld\n", ++cas, f(n, m, k));
}
return 0;
}
然而cf和hdu上这题都不支持__int128,过不了……
不过https://blog.csdn.net/xiji333/article/details/101159844这个方法可以避开__int128