lightoj1097 lucky number 報數類問題 線段樹的單點更新

題目鏈接:http://www.lightoj.com/volume_showproblem.php?problem=1097

題目:

Lucky numbers are defined by a variation of the well-known sieve of Eratosthenes. Beginning with the natural numbers strike out all even ones, leaving the odd numbers 1, 3, 5, 7, 9, 11, 13, ... The second number is 3, next strike out every third number, leaving1, 3, 7, 9, 13, ... The third number is 7, next strike out every seventh number and continue this process infinite number of times. The numbers surviving are called lucky numbers. The first few lucky numbers are:

1, 3, 7, 9, 13, 15, 21, 25, 31, 33, ...

In this problem your task is to find the nth lucky number where n is given in input.

Input

Input starts with an integer T (≤ 10000), denoting the number of test cases.

Each case contains an integer n (1 ≤ n ≤ 105).

Output

For each case, print the case number and the nth lucky number.

Sample Input

Output for Sample Input

2

2

100000

Case 1: 3

Case 2: 1429431



分析:這個題給我一種約瑟夫環的問題的感覺,但人家約瑟夫是一個環,算了我們姑且叫它報數問題。這種報數問題感覺就是一個在剩餘序列中的相對位置和在原序列中絕對位置的關係。我們用sum數組存儲在某個區間上剩餘元素的個數,我們發現線段樹這一數據結構可以很好的解決這一類問題。

上面這道題中,我們發現實質上每次需要報的數就是從剩餘序列中相對位置爲2,3,... ...的數對應都在原序列中絕對位置的數。算了好長一句話還是看代碼吧!唔,不過這代碼還不完善,因爲邊界沒處理好,如果大佬有解決方法懇求指點。

code:

#include<cstdio>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int MAXN=2e6;
int sum[(MAXN+5)<<2];int ans[MAXN],visit[MAXN];
//sum是用來維持某區間上剩餘數的個數,葉子節點也是區間,只不過左右區間重合了
void PushUp(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
    if(l==r){
        sum[rt]=l&1;//因爲原始的序列沒有偶數,只有奇數
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
//通過在剩餘序列中的相對位置p,去原序列中找到絕對位置l,並刪除它
int update(int p,int l,int r,int rt){
    if(l==r){
        sum[rt]=0;
        return l;
    }
    int pos;
    int m=(l+r)>>1;
    if(sum[rt<<1]>=p)pos=update(p,lson);
    else pos=update(p-sum[rt<<1],rson);
    PushUp(rt);
    return pos;
}


//功能與上面的update類似,只不過只詢問,不刪除
int Query(int p,int l,int r,int rt){
    if(l==r){
        return l;
    }
    int pos;
    int m=(l+r)>>1;
    if(sum[rt<<1]>=p)pos=Query(p,lson);
    else pos=Query(p-sum[rt<<1],rson);
    return pos;
}
void init(){//打表,初始化
    build(1,MAXN,1);
    memset(visit,0,sizeof(visit));
    memset(ans,0,sizeof(ans));
    for(int i=2;i<=MAXN;++i){//i爲每次報的數在剩餘序列中的相對位置
        if(i>sum[1])return;//現在根本沒那麼多人辣!根節點表示剩餘序列中還有多少人!
        int k=Query(i,1,MAXN,1);//查詢相對位置i的絕對位置
        int ID=k;//需要刪除的人的編號
        for(;;){
            int pos=update(ID,1,MAXN,1);
            visit[pos]=1;//visit數組記錄這個人已經被刪除了,方便後來打表
            if(pos+ID>MAXN)break;//每次單向循環的終止條件沒處理好。
            ID=ID+k-1;//刪除一個人後第ID個人,第n(n>ID)個人就變成了第n-1個人
        }
    }
}
int main(void){
    //打表
    init();
    for(int i=1,j=1;i<=MAXN;++i){
        if((i&1)&&!visit[i]){
            ans[j++]=i;
        }
    }


    int T;scanf("%d",&T);
    int CASE=0;
    while(T--){
        int k;scanf("%d",&k);
        printf("Case %d: %d\n",++CASE,ans[k]);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章