POJ 2886 Who Gets the Most Candies?

题目:http://poj.org/problem?id=2886

题意:

有一个n个人围成圆圈,每次有一个人出圈,下一次出圈的人由本次出圈的人决定。

按照出圈的顺序,每个人可以获得不同的糖果,找出可以获得最多糖果的人。

 第p个出圈的人可以获得F(p)个糖果。 

思路:

用线段树维护剩余的人数。

约瑟夫环+线段树+反素数

#include <iostream>
#include <cstdio>

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

using namespace std;
const int maxn = 500005;
int tree[maxn << 2];
struct child{
    char name[20];
    int card;
}cc[maxn];

void PushUp(int rt){
    tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}

void build(int l, int r, int rt){
    tree[rt] = r-l+1;
    if (l == r)
        return;
    int  m = (l + r) >> 1;
    build (lson);
    build (rson);
    PushUp(rt);
}

void update(int p, int l, int r, int rt){
    if (l == r) {
        tree[rt] = 0; return;
    }
    int m = (l + r) >> 1;
    if (m >= p) update(p,lson);
    else update(p, rson);
    PushUp(rt);
}

int query(int k, int l, int r, int rt){
    if (l == r)
        return l;
    int m = (l + r) >> 1;
    if (tree[rt << 1] >= k)
        return query(k, lson);
    else
        return query(k - tree[rt << 1], rson);
}
int hash[maxn];
void init(){//打出反素数表
    for (int i = 1; i <= maxn; i ++){
        hash[i] ++;
        for (int j = 2*i; j <= maxn; j += i)
            hash[j] ++;
    }
}
int main(){
    init();
    int n, k;
    while (scanf ("%d %d", &n, &k) != EOF){
        
        for(int i = 1;i <= n;i++)
            scanf("%s%d",cc[i].name,&cc[i].card);
        
        build (1, n, 1);
        
        int maxtemp = hash[1];
        int kk = 1;
        for(int i = 2;i <= n;i++){
            if(maxtemp<hash[i]){
                maxtemp = hash[i];
                kk = i;
            }
        }
        int pos = k; //k为人在约瑟夫环中的相对位置,pos为该人在最初的环中的绝对位置
        for(int i = 1;i < kk;i++) {
            update (pos, 1, n, 1); //删除当前这个人
            //求出下一个人的在约瑟夫环中的相对位置
            k += cc[pos].card;
            if (cc[pos].card > 0) k--; //注意:当num[pos]>0时,pos后面的人的相对位置都要减1
            k = (k % tree[1] + tree[1]) % tree[1]; //sum[1]为剩余人的人数
            if (k == 0) k = tree[1];
            //由相对位置求出绝对位置
            pos = query(k, 1, n, 1);
        }
        printf ("%s %d\n", cc[pos].name,hash[kk]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值