题目链接
大意是
n
n
n个人围成一圈(ID依次为
1
∼
n
1\sim n
1∼n),每
k
k
k个人踢掉1个,求第
m
m
m个被踢掉的人的ID。
1. O ( k log n ) O(k\log n) O(klogn)解法(未AC)
参考知乎回答
按该回答中的方式进行编号,第
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]。
用上述式子不断迭代,最后求出的就是该玩家的id。
记
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
(
k
log
n
)
O(k\log n)
O(klogn)。
可以写出如下代码:
#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;
}
但是无法通过这题。
2. O ( min ( m , k log n ) ) O(\min(m,k\log n)) O(min(m,klogn))解法
记
f
(
n
,
m
,
k
)
f(n,m,k)
f(n,m,k)为问题的答案(0和
n
n
n等价),则有递推式
f
(
n
,
m
,
k
)
=
k
+
f
(
n
−
1
,
m
−
1
,
k
)
m
o
d
n
f(n,m,k)=k+f(n-1,m-1,k) \mod n
f(n,m,k)=k+f(n−1,m−1,k)modn。
复杂度
O
(
m
)
O(m)
O(m)。
若
n
>
k
n>k
n>k,那么上述方法中多次取模是没有必要的,可以改为批量处理:
f
(
n
,
m
,
k
)
=
k
x
+
f
(
n
−
x
,
m
−
x
,
k
)
m
o
d
n
(
k
x
+
f
(
n
−
x
,
m
−
x
,
k
)
≤
n
+
k
−
1
)
f(n,m,k)=kx+f(n-x,m-x,k) \mod n\\(kx+f(n-x,m-x,k)\le n+k-1)
f(n,m,k)=kx+f(n−x,m−x,k)modn(kx+f(n−x,m−x,k)≤n+k−1)
复杂度
O
(
min
(
m
,
k
log
n
)
)
O(\min(m,k\log n))
O(min(m,klogn))。
AC代码如下:(要写成非递归形式,否则栈空间不够)
#include <bits/stdc++.h>
#define rep(i, l, r) for(int i=l; i<=r; ++i)
#define per(i, r, l) for(int i=r; i>=l; --i)
#define ll long long
using namespace std;
ll f(ll n, ll m, ll k){
if(k == 1) return m;
ll ret = 0, x = 1, t = m;
while(t){
t -= x;
ret = (k * x + ret) % (n - t);
if(!ret) ret = n - t;
x = min(t, (n - t - ret) / (k - 1) + 1);
}
return ret;
}
int main() {
int T, cas=0;
scanf("%d",&T);
while(T--) {
ll n, m, k;
scanf("%lld%lld%lld", &n, &m, &k);
printf("Case #%d: %lld\n", ++cas, f(n, m, k));
}
return 0;
}