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
",e[pos].name,MAX);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/KirinSB/p/9408753.html