首页 > 代码库 > 【codevs1282】约瑟夫问题

【codevs1282】约瑟夫问题

题目描述

有编号从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相同,因为已经减少一个。

因此rn初始值为1。

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <algorithm>
 4 using namespace std;
 5 int l[30001] , r[30001] , num[30001] , si[30001] , rnd[30001] , tot , root;
 6 void pushup(int k)
 7 {
 8     si[k] = si[l[k]] + si[r[k]] + 1;
 9 }
10 void zig(int &k)
11 {
12     int t = l[k];
13     l[k] = r[t];
14     r[t] = k;
15     si[t] = si[k];
16     pushup(k);
17     k = t;
18 }
19 void zag(int &k)
20 {
21     int t = r[k];
22     r[k] = l[t];
23     l[t] = k;
24     si[t] = si[k];
25     pushup(k);
26     k = t;
27 }
28 void ins(int &k , int x)
29 {
30     if(!k)
31     {
32         k = ++tot;
33         num[k] = x;
34         si[k] = 1;
35         rnd[k] = rand();
36         return;
37     }
38     si[k] ++ ;
39     if(x < num[k])
40     {
41         ins(l[k] , x);
42         if(rnd[l[k]] < rnd[k])
43             zig(k);
44     }
45     else
46     {
47         ins(r[k] , x);
48         if(rnd[r[k]] < rnd[k])
49             zag(k);
50     }
51 }
52 void del(int &k , int x)
53 {
54     if(!k) return;
55     if(x == num[k])
56     {
57         if(l[k] * r[k] == 0)
58             k = l[k] + r[k];
59         else if(rnd[l[k]] < rnd[r[k]])
60             zig(k) , del(k , x);
61         else
62             zag(k) , del(k , x);
63     }
64     else if(x < num[k])
65         si[k] --  , del(l[k] , x);
66     else
67         si[k] --  , del(r[k] , x);
68 }
69 int getrank(int k , int x)
70 {
71     if(x == num[k]) return si[l[x]] + 1;
72     else if(x < num[k]) return getrank(l[k] , x);
73     else return getrank(r[k] , x) + si[l[x]] + 1;
74 }
75 int find(int k , int x)
76 {
77     if(x <= si[l[k]]) return find(l[k] , x);
78     else if(x > si[l[k]] + 1) return find(r[k] , x - si[l[k]] - 1);
79     else return num[k];
80 }
81 int main()
82 {
83     int n , m , i , rn = 1 , c;
84     scanf("%d%d" , &n , &m);
85     for(i = 1 ; i <= n ; i ++ )
86         ins(root , i);
87     for(i = 1 ; i <= n ; i ++ )
88     {
89         rn = (rn + m - 2 + si[root]) % si[root] + 1;
90         c = find(root , rn);
91         printf("%d " , c);
92         del(root , c);
93     }
94     printf("\n");
95     return 0;
96 }

【codevs1282】约瑟夫问题