【codevs1282】约瑟夫问题 Treap

题目描述

有编号从1到N的N个小朋友在玩一种出圈的游戏。开始时N个小朋友围成一圈,编号为I+1的小朋友站在编号为I小朋友左边。编号为1的小朋友站在编号为N的小朋友左边。首先编号为1的小朋友开始报数,接着站在左边的小朋友顺序报数,直到数到某个数字M时就出圈。直到只剩下1个小朋友,则游戏完毕。

现在给定N,M,求N个小朋友的出圈顺序。

输入

唯一的一行包含两个整数N,M。(1<=N,M<=30000)

输出

唯一的一行包含N个整数,每两个整数中间用空格隔开,第I个整数表示第I个出圈的小朋友的编号。

样例输入

5 3

样例输出

3 1 5 2 4


很好想的一道题,就是求出每次需要出圈的人的排名,然后输出并删除。

然而N为30000怎么办?

网上的题解是线段树,然而线段树不能删除,过于麻烦。

于是想到Treap。

代码有点长,但很好理解。

需要注意rn是上次的排名,但是这次第一个人的排名却应该与rn相同,因为已经减少一个人,对应排名-1。

因此rn初始值为1。

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
int l[30001] , r[30001] , num[30001] , si[30001] , rnd[30001] , tot , root;
void pushup(int k)
{
    si[k] = si[l[k]] + si[r[k]] + 1;
}
void zig(int &k)
{
    int t = l[k];
    l[k] = r[t];
    r[t] = k;
    si[t] = si[k];
    pushup(k);
    k = t;
}
void zag(int &k)
{
    int t = r[k];
    r[k] = l[t];
    l[t] = k;
    si[t] = si[k];
    pushup(k);
    k = t;
}
void ins(int &k , int x)
{
    if(!k)
    {
        k = ++tot;
        num[k] = x;
        si[k] = 1;
        rnd[k] = rand();
        return;
    }
    si[k] ++ ;
    if(x < num[k])
    {
        ins(l[k] , x);
        if(rnd[l[k]] < rnd[k])
            zig(k);
    }
    else
    {
        ins(r[k] , x);
        if(rnd[r[k]] < rnd[k])
            zag(k);
    }
}
void del(int &k , int x)
{
    if(!k) return;
    if(x == num[k])
    {
        if(l[k] * r[k] == 0)
            k = l[k] + r[k];
        else if(rnd[l[k]] < rnd[r[k]])
            zig(k) , del(k , x);
        else
            zag(k) , del(k , x);
    }
    else if(x < num[k])
        si[k] --  , del(l[k] , x);
    else
        si[k] --  , del(r[k] , x);
}
int getrank(int k , int x)
{
    if(x == num[k]) return si[l[x]] + 1;
    else if(x < num[k]) return getrank(l[k] , x);
    else return getrank(r[k] , x) + si[l[x]] + 1;
}
int find(int k , int x)
{
    if(x <= si[l[k]]) return find(l[k] , x);
    else if(x > si[l[k]] + 1) return find(r[k] , x - si[l[k]] - 1);
    else return num[k];
}
int main()
{
    int n , m , i , rn = 1 , c;
    scanf("%d%d" , &n , &m);
    for(i = 1 ; i <= n ; i ++ )
        ins(root , i);
    for(i = 1 ; i <= n ; i ++ )
    {
        rn = (rn + m - 2 + si[root]) % si[root] + 1;
        c = find(root , rn);
        printf("%d " , c);
        del(root , c);
    }
    printf("
");
    return 0;
}
原文地址:https://www.cnblogs.com/GXZlegend/p/6258646.html