约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。通常解决这类问题时我们把编号从0~n-1,最后 [1] 结果+1即为原问题的解。

有递推公式 f n = ( f n − 1 + k ) % n f_n=(f_{n-1}+k)\%n fn=(fn1+k)%n f n f_n fn表示n个人按k的取法最后一个人的编号,这个公式的证明:
首先我们是在知道 f n − 1 f_{n-1} fn1的情况下,比如1 2 3 4 5 k=3,第一轮取了3,剩下编号是1 2 4 5,但是其实我们第二轮取的时候编号是从4开始编为1的,也就是3 4 1 2,对比这两个序列,可以看出这个老的序列( f n f_n fn)就是新序列( f n − 1 f_{n-1} fn1)+k再取模n的

可以递归也可以不递归

int solve(int n,int k){
   
	for(int i=1;i<=n;i++){
   
		f[i]=(f[i-1]+k)%i;
	}
	return f[n]+1;
}

这个算是约瑟夫环的变形吧,就是每一轮删完不是从下一个删,而是从第一个开始,比如1-10,k=3,第一轮删除3,6,9之后,不是从10开始算1,而是从1开始,没有形成一个环
hdu2211

#include <bits/stdc++.h>
using namespace std;
int t;
long long n,k;
//函数返回的就是胜利者编号
long long cir(long long n,long long m){
   
    //递归边界
    if(n==k){
   
        return k;
    }
    //一轮过去后剩下n-n/k人
    //x就是下一轮也就是最后胜利者的编号
    long long x=cir(n-n/k,k);
    //在这一轮的编号t=(x-1)-(x-1)/k+1再化简
    return (x-1)/(k-1)+x;
}
int main(void){
   
    scanf("%d",&t);
    while(t--){
   
        scanf("%lld%lld",&n,&k);
        printf("%lld\n",cir(n,k));
    }
    return 0;
}