1. 程式人生 > >[Codevs] 1282 約瑟夫問題

[Codevs] 1282 約瑟夫問題

分析 輸入 pla 根據 快的 bsp 前綴和 head 約瑟夫問題

1282 約瑟夫問題

時間限制: 1 s 空間限制: 128000 KB 題目等級 : 大師 Master 題目描述 Description

有編號從1到N的N個小朋友在玩一種出圈的遊戲。開始時N個小朋友圍成一圈,編號為I+1的小朋友站在編號為I小朋友左邊。編號為1的小朋友站在編號為N的小朋友左邊。首先編號為1的小朋友開始報數,接著站在左邊的小朋友順序報數,直到數到某個數字M時就出圈。直到只剩下1個小朋友,則遊戲完畢。

現在給定N,M,求N個小朋友的出圈順序。

輸入描述 Input Description

唯一的一行包含兩個整數N,M。(1<=N,M<=30000)

輸出描述 Output Description

唯一的一行包含N個整數,每兩個整數中間用空格隔開,第I個整數表示第I個出圈的小朋友的編號。

樣例輸入 Sample Input

5 3

樣例輸出 Sample Output

3 1 5 2 4

分析 Analysis

現在有一個標準的1-n的遞增排列

擊鼓傳花,每次數到 m 時就要去掉當前這個元素,然後繼續從 0 計數

那麽定義 sum( i ) 為元素 i 之前的當前實際存在的元素數,在計算過程中,sum( i ) 才是真正的位置

那麽給每一個元素一個初始權值 1 ,維護每個元素以自己為端點的前綴和,就能愉快的計算 sum( i ) 啦

那麽設 pos 為當前要操作的元素位置,根據 AET(Apparently Easy Theory) 原理,我們知道下一步的 pos = (pos-1)%len

但是我們計算的時候要把pos+1

那麽線段樹的內容就是維護前綴和且單點置零啦

註意查找

這次的線段樹被閹割的非常閹割

代碼 Code

技術分享
 1 #include<cstdio>
 2 #include<iostream>
 3 #define mid (L+R)/2
 4 #define
lc (rt<<1) 5 #define rc (rt<<1|1) 6 #define maxn 1000000 7 using namespace std; 8 9 int Tree[maxn],n,m; 10 void maintain(int rt){Tree[rt] = Tree[lc]+Tree[rc];} 11 void build(int rt,int L,int R){ 12 if(L == R) Tree[rt] = 1; 13 else{ 14 build(lc,L,mid); 15 build(rc,mid+1,R); 16 maintain(rt); 17 } 18 } 19 void modify(int rt,int L,int R,int pos){ 20 if(L == R) Tree[rt] = 0; 21 else{ 22 if(pos <= mid) modify(lc,L,mid,pos); 23 else modify(rc,mid+1,R,pos); 24 maintain(rt); 25 } 26 } 27 int query(int rt,int L,int R,int val,int remain){ 28 if(L == R) return L; 29 else{ 30 if(val <= Tree[lc]+remain) return query(lc,L,mid,val,remain); 31 else return query(rc,mid+1,R,val,remain+Tree[lc]); 32 } 33 } 34 35 int main(){ 36 scanf("%d%d",&n,&m); 37 38 build(1,1,n); 39 40 int pos = m,ans; 41 for(int i = n;i >= 1;i--){ 42 pos = (pos-1)%(Tree[1]); 43 // printf("###$$%d ",pos+1); 44 ans = query(1,1,n,pos+1,0); 45 printf("%d ",ans); 46 modify(1,1,n,ans); 47 pos += m; 48 // getchar(); 49 } 50 51 return 0; 52 }
閹割的非常嚴重的線段樹

[Codevs] 1282 約瑟夫問題