【gym 101955K】Let the Flames Begin(约瑟夫环问题)

题目链接
约瑟夫环算法参考
按上述回答中的方式进行编号,如果是 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的情况如图:
【gym 101955K】Let the Flames Begin(约瑟夫环问题)
可知编号 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

上一篇:Android单SIM卡、ROM64位等的配置


下一篇:笔记-CF1354E Graph Coloring