POJ 2886 Who Gets the Most Candies? (線段樹)題解

題意:一堆小朋友圍成一個圈,規定從k開始玩,每個被選中的人都有一個數字,正數代表從他左邊開始數num,負數從右邊數,被選中的人繼續按照上述操作,直到都退出圈子,第i個退圈的人能拿到一個點數,這個點數是i的因數個數(比如第4個人拿3點)。問:誰點數最多,一樣多選第一個。

思路:一道好題啊,萬萬沒想到是線段樹,想要想到感覺還是有點難度的。這道題其實就是要一直維護剩餘人數,但是以前沒做過線段樹維護剩餘人數的,一時半會想不到...首先,我們要用打表找到所有點數。然後用線段樹維護剩餘人數,這裏有一個剩餘人數位置和所有人數位置轉換的過程,是用線段樹解決的。線段樹的每個節點儲存的是某個區間的剩餘人數,所以我們在用一個人在剩餘人中的位置找到他的編號時,是比較sum節點而非L,R。還有向左向右方向要注意,畫個圈慢慢感受。具體看註釋。

代碼:

#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define ll long long
const int maxn = 500000+5;
const int MOD = 1e7;
const int INF = 0x3f3f3f3f;
using namespace std;
struct node{
    char name[12];
    int num;
}e[maxn];
int get[maxn],sum[maxn << 2];
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] = 1;
        return;
    }
    int m = (l + r) >> 1;
    build(l,m,rt << 1);
    build(m + 1,r,rt << 1 | 1);
    pushup(rt);
}
int update(int pos,int l,int r,int rt){
    if(l == r){
        sum[rt] = 0;
        return l;
    }
    int ans;
    int m = (l + r) >> 1;
    if(pos <= sum[rt << 1]) //pos代表在剩餘人數中的第幾個,所以寫法略有不同
        ans = update(pos,l,m,rt << 1);
    else
        ans = update(pos - sum[rt << 1],m + 1,r,rt << 1 | 1);
    pushup(rt);
    return ans;
}
void init(){
    int top = 500000;
    memset(get,0,sizeof(get));
    for(int i = 1;i <= top;i++){
        get[i]++;
        for(int j = 2*i;j <= top;j += i){
            get[j]++;
        }
    }
}
int main(){
    init();
    int n,k;
    while(~scanf("%d%d",&n,&k)){
        build(1,n,1);
        for(int i = 1;i <= n;i++)
            scanf("%s%d",e[i].name,&e[i].num);
        int aim = 0,MAX = -1;
        for(int i = 1;i <= n;i++){
            if(get[i] > MAX){
                aim = i;
                MAX = get[i];
            }
        }
        int pos;    //開始計算的位置
        int mov,all,now = 0;
        while(true){
            pos = update(k,1,n,1);    //被宰的是哪個人
            now++;
            if(now == aim) break;
            mov = e[pos].num;
            if(mov > 0){    //在左邊
                k = (k - 1 + mov - 1)%sum[1] + 1;   //這裏的pos是剩餘人中的編號
                //第一個-1是因爲pos這個人已經被宰了,所以他這個位置其實是沒有的
                //第二個-1是防止%之後歸0,所以先-1再+1
            }
            else{   //在右邊
                k = ((k - 1 + mov)%sum[1] + sum[1])%sum[1] + 1;
            }
        }
        printf("%s %d\n",e[pos].name,MAX);
    }
    return 0;
}

 

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