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 ; }
重要的是自信,一旦有了自信,人就会赢得一切。