2018-2019 ACM-ICPC 沈阳赛区 K. Let the Flames Begin

K. Let the Flames Begin

题目链接:https://codeforces.com/gym/101955/problem/K

题意:

n个人围成一个圈,然后依次从1开始报数,报到k的人出局,现在有个人,他想要成为第m个出局的男人,现在要求他初始位置应该在哪。

 

题解:

类似于约瑟夫环问题,所以我们可以考虑一下递推。我们可以考虑一下n和n - 1规模的关系,借助于约瑟夫环的思路,我们可以知道,如果一个人在n - 1规模时位置为p,那么n的规模时位置应为(p + k) % n (下标从0 ~ n - 1)。我们设第m轮出局的位置为f(n,m),那么就有f(n,m) = (f(n - 1 , m - 1) + k ) % n。

但是这里n,m,k可能到10^18,数据范围过大,直接递推行不通。但是题目中有个条件为min(m,k) <= 2e6, 那么我们就需要利用上这个信息。

当m <= k 时,我们直接从f(n - m + 1 , 1)开始递推即可,题中数据保证这样不会超时;

当m > k时,这个k可能远小于n,也就是取模操作困难隔很久才一次,所以我们可以根据这个加速递推。设c轮过后会取模,那么有f(a,b) + c * k >= a   =>   c >= (a - f(a,b)) / k,我们直接通过计算得出这个c就好了~

这个题主要就是在于对递推式的理解吧~

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n,m,k;
int T;
int main() {
    ios::sync_with_stdio(false);cin.tie(0) ;
    int cnt = 0;
    cin >> T;
    while(T--) {
        cnt++;
        cin >> n >> m >> k ;
        cout << "Case #" << cnt << ": " ;
        ll p = n - m + 1;
        ll ans = (k - 1) % p ;
        if(m < k) {
            for(int i = 2; i <= m ; i++)
                ans = (ans + k) % (++p);
        }else {
            ll a = n - m + 1;
            if(k == 1) {
                cout << m << '\n' ;
                continue ;
            }
            while(a < n) {
                ll x = (ll)ceil(1.0 * (a - ans) / (k - 1));
                if(a + x > n) x = n - a;
                ans = (ans + x * k ) % (p = a + x) ;
                a = a + x ;
            }
        }
        cout << ans + 1<< '\n' ;
    }
    return 0 ;
}

 

posted @ 2019-04-09 22:11  heyuhhh  阅读(766)  评论(0编辑  收藏  举报