Let the Flames Begin(约瑟夫环)

题目描述

Tonight n young men are going to participate in Peter’s campfire party. They decide to play an ancient counting-out game which was first described by Titus Flavius Josephus. Here is a brief introduction to the game.
Before starting the game, these young men will stand in a circle around the campfire and the first man to join the circle will start the game. Counting will begin at the first man and proceed around the circle in the counterclockwise direction repeatedly. That is, the first man will report one at the beginning, and the second one in the counterclockwise direction will report two, and so forth, until a poor man reports k and consequently leaves the circle to become a bystander. The game will be repeated with the remaining men, restarting from the next man in the counterclockwise direction who will be the new first man, going in the same direction, until all the young men have left the circle.
Peter wanna be the m-th one who left the circle since he strongly believes this number is lucky for him.
As a sophisticated programmer, can you point out the right place he should stand at before the game start so that he can achieve his goal?
For the sake of clarity, we assume the index of the first man to join the circle is 1, the index of the next man in his counterclockwise direction is 2, and so on. By the definition, the index of the last man in that direction should be n, and your task is to determine the index of the place Peter wants.

输入

The input contains several test cases, and the first line contains a positive integer T indicating the number of test cases which is up to 1000.
For each test case, the only line contains three integers n, m and k where 1 ≤ n, m, k ≤ 1018 and n ≥ m.
We guarantee that the sum of min { m, k } (i. e. the minimum of m and k) in all test cases is no larger than 2 × 106.

输出

For each test case, output a line containing “Case #x: y” (without quotes), where x is the test case number starting fro

m 1, and y is the index of the right place.

思路:

不同于求最后一个出圈的,此题求第m个出圈的

对于求最后一个有O(n)的递推式:

f(n)=(f(n-1)+k)%n;

 可用循环求解 

ans=0;
for(int i=1;i<=n;i++) ans=(ans+k)%i;

 当n很大时,O(n)的复杂度就不够了。考虑到不是每次都会%i,可以把其看成一个追击相遇问题

i每次增加1,ans每次增加k,看多久ans会超过i,大大降低复杂度。

i=1,ans=0;
while(i<n)
	{
		p=(i-ans)/(k-1)+((i-ans)%(k-1)!=0);//距离 除以 速度的差 得到 时间
		if(i+p>n)p=n-i;//要判断是否大于n
		ans+=p*k;//ans走
		i+=p;//i走
		ans%=i;//取模
	}
//i从1开始

 对于此题也有递推公式:

A(n,m)=(A(n−1,m−1)+k)%n

数据范围小可以递归求解,对于大数据范围类比求最后一个的思路

先求出A(n+m-1,1)=k,然后类似与求最后一个的情况

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int idx;
ll F(ll n,ll m,ll k)
{
    if(m==1) return (k-1)%n;
    else return (F(n-1,m-1,k)+k)%n;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--)
    {
        ll n,m,k;
        cin>>n>>m>>k;
        if(k==1) {cout<<"Case #"<<++idx<<": "<<m<<'\n';continue;}
        if(m<1e6) {cout<<"Case #"<<++idx<<": "<<F(n,m,k)+1<<'\n';continue;}
        ll i=n-m+1;
        ll ans=(k-1)%i;
        while(i<n)
        {
            ll tt=(i-ans)/(k-1)+((i-ans)%(k-1)!=0);
            if(i+tt>n) tt=n-i;
            ans+=tt*k;
            i+=tt;
            ans%=i;
        }
        cout<<"Case #"<<++idx<<": "<<ans+1<<'\n';
    }
    return 0;
}

递推式的正确性是基于下标从零开始的,所以最后答案记得加1.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值