约瑟夫环

(3 mins to read)

n个人(编号从0开始),从1开始依次报数,报到k的那个人退出,并继续循环报数,问最后剩下的人的编号这个问题还是要从递归的角度去考虑设$f_{n-1}$​表示n个人时最后剩下的那个人那么对于n个人的情况,第一个出局的人是k-1,之后就相当于是从k开始的n-1个人的约瑟夫环问题所以很容易得到递推式$f_n = (f_{n-1} + k)%n$同样的如果要求第m个出局的人,也可以用上述公式$f_{n,m}=(f_{n-1,m-1}+k)%n$对于$k \ll n$的情况,考虑$f_{i+x-1}=(f_{i-1}+x*k)%(i+x-1)$,当$f_{i-1}+x*k\leq i+x-1$时不会取模,所以快速递推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ll J(ll n, ll k)
{
if(k==1) return n;
ll ans = 0, i = 2;
while(i<=n)
{
ll cnt = (i-ans-2)/(k-1);
if(!cnt)
{
ans = (ans + k)%i;
i++;
}
else
{
if(i+cnt>n)
{
ans = (ans + k*(n-i+1))%n;
break;
}
else
{
i += cnt;
ans = (ans + k*cnt)%i;
}
}
}
return ans + 1;
}

另外对于k=2的特殊情况,可以考虑当n为2的幂次的时候答案就是1,所以当n为$2^x+y$的形式时,答案为$2*y+1$

求第x个人是第几个死的(0下标)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ll solve(ll n, ll k, ll x)
{
ll ans = 0;
while(n)
{
x = (x+n)%n;
if(k>n) x += (k-x-1+n-1)/n*n;
if((x+1)%k==0)
{
ans += (x+1)/k;
break;
}
else
{
if(k>n)
{
ans += x/k;
ll tmp = x;
x = x-(x/n+1)*(x/k)+(x+n)/n*n-k;
n -= tmp/k;
}
else
{
ans += n/k;
x = x-x/k;
x += n-n/k*k;
n -= n/k;
}
}
}
return ans;
}