HDU_3089_约瑟夫环快速递推

HDU_3089_约瑟夫环快速递推

链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3089

题意:
约瑟夫环问题。给出 n n n 个人,从 1 1 1 n n n 编号, n n n 个人围成一个圈。第一个人从 1 1 1 开始报数,数到 k k k 的人杀掉,然后从下一个人开始重新报数,如此循环,直到只剩下一个人,问幸存的人的编号。

思路:

传统约瑟夫环问题:可以这么想,每减少一个人,相当于把序列整体循环左移 k k k 位,也就是说,如果设存有 i i i 个人时幸存者的序号为 f ( i ) f(i) f(i) ,那么如果我们已知还留存有 i − 1 i-1 i1个人时幸存者的序号 f ( i − 1 ) f(i-1) f(i1) ,那么逆推状态 f ( i ) = ( f ( i − 1 ) + k ) % i f(i) = (f(i-1)+k)\%i f(i)=(f(i1)+k)%i + k +k +k 是右移,因为会超范围,所以模 i i i,使得数据都在范围 0 → i − 1 0 \to i-1 0i1,因为取模我们从 0 0 0 开始报数,求得结果加一即可)。只剩下一个人时, f ( 1 ) = 0 f(1) = 0 f(1)=0 。时间复杂度 O ( n ) O(n) O(n)
状态转移方程:
f ( i ) = { 0   i = 1   ( f ( i − 1 ) + k ) % i   i ∈ 2... n   f(i)= \begin{cases} 0 & \text { $i = 1$ } \\ (f(i-1) + k)\%i & \text{ $i \in 2...n$ } \end{cases} f(i)={0(f(i1)+k)%i i= i2...n 

幸 存 者 编 号 : a n s = f ( n ) + 1 幸存者编号: ans = f(n) + 1 ans=f(n)+1

本题:但是这道题的 n n n 非常大,看似无法用上述算法。但是这题的 k k k 很小,只有 1000 1000 1000。观察上式可以发现 k k k 比较小的时候递推后期可能加若干个 k k k 才会取模。那么我们是不是可以多步递推来加速递推式?没错,就是这样。

  1. f ( i − 1 ) + k ≥ i f(i-1)+k \geq i f(i1)+ki 时,直接递推。
  2. f ( i − 1 ) + k < i f(i-1)+k < i f(i1)+k<i 时,加速优化。

那么可以加速多少步呢?设加速步数为 x x x
{ f ( i ) = ( f ( i − 1 ) + k ) % i      .      .      . f ( i + x − 1 ) = ( f ( i − 1 ) + x ∗ k ) % ( i + x − 1 ) \begin{cases} f(i) = (f(i-1)+k)\%i \\ \ \ \ \ . \\ \ \ \ \ . \\ \ \ \ \ . \\ f(i+x-1) = (f(i-1) + x*k)\%(i+x-1) \end{cases} f(i)=(f(i1)+k)%i    .    .    .f(i+x1)=(f(i1)+xk)%(i+x1)
f ( i − 1 ) = a n s f(i-1) = ans f(i1)=ans

a n s + x ∗ k < i + x − 1 ans + x*k < i+x-1 ans+xk<i+x1 时,不会取模,这时加速递推( x < i − a n s − 1 k − 1 x < \frac{i-ans-1} {k-1} x<k1ians1)

注意:

  1. i − a n s − 1 i-ans-1 ians1 能整除 k − 1 k-1 k1 时, x = i − a n s − 1 k − 1 − 1 x = \frac{i-ans-1} {k-1}-1 x=k1ians11,否则 x = i − a n s − 1 k − 1 x = \frac{i-ans-1} {k-1} x=k1ians1
  2. 加速递推时, i + x − 1 i+x-1 i+x1 可能会大于等于 n n n ,这时直接终止。

在这里插入图片描述
A C 代 码 AC代码 AC

#include 
using namespace std;
typedef long long LL;

LL n, k;
LL ans, x;

int main(int argc, char const *argv[])
{
    while(cin>>n>>k) {
        if(k == 1) {
            cout<<n<<endl;
            continue;
        }
        ans = 0;
        for (LL i = 2; i <= n; i+=x) {
            if(ans + k >= i) { // 直接递推
                x = 1;
            }
            else { // 加速优化
                if((i-ans-1)%(k-1) == 0) {
                    x = (i-ans-1)/(k-1)-1;
                }
                else {
                    x = (i-ans-1)/(k-1);
                }
                if(i+x-1 >= n) {
                    ans = ans + (n-i+1)*k;
                    break;
                }
            }
            ans = (ans + k * x)%(i+x-1);
        }
        cout<<ans+1<<endl;
    }
    return 0;
}

你可能感兴趣的:(ACM,HDU)