POJ 2886 Who Gets the Most Candies? (线段树 约瑟夫环问题变种)

题目大意:

就是现在有一些孩子坐成一圈, 每个人手里有一个数字(正数或者负数), 孩子们编号1~n, n<=50W

那么, 第一次由编号为K的人跳出, 然后根据其手上的数顺时针或者逆时针决定下一个人

第p个出去的人能得到p的约数个糖果, 问那个人得到的糖果最多


大致思路:

就是约瑟夫环的变种, 每次可能顺时针或者逆时针数出下一个人

那么用线段树的结点表示对应区间内还剩下的人的个数

于是维护区间和就可以了

至于当前在pos位置, 想左边或者向右边移动, 用相对位置来求解

即如果当前要退出的人在剩下的sum个人中的第p个位置, 其退出后剩下sum - 1人, 那么他变成这个中的相对第几个是可以O(1)推导出来的

然后根据这个值(也就是前缀和)找到线段树对应的位置即可


代码如下:

Result  :  Accepted     Memory  :  14040 KB     Time  :  1688 ms

/*
 * Author: Gatevin
 * Created Time:  2015/8/15 23:28:03
 * File Name: Sakura_Chiyo.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define maxn 500050

struct Segment_Tree
{
    #define lson l, mid, rt << 1
    #define rson mid + 1, r, rt << 1 | 1
    int val[maxn << 2];
    void pushUp(int rt)
    {
        val[rt] = val[rt << 1] + val[rt << 1 | 1];
        return;
    }
    void build(int l, int r, int rt)
    {
        if(l == r)
        {
            val[rt] = 1;
            return;
        }
        int mid = (l + r) >> 1;
        build(lson);
        build(rson);
        pushUp(rt);
        return;
    }
    void update(int l, int r, int rt, int pos, int value)
    {
        if(l == r)
        {
            val[rt] = value;
            return;
        }
        int mid = (l + r) >> 1;
        if(mid >= pos) update(lson, pos, value);
        else update(rson, pos, value);
        pushUp(rt);
    }
    int query(int l, int r, int rt, int value)//询问前缀和为k的为1(最左)位置
    {
        if(l == r)
            return l;
        int mid = (l + r) >> 1;
        if(val[rt << 1] < value) return query(rson, value - val[rt << 1]);
        else return query(lson, value);
    }
};

Segment_Tree ST;
int cnt[maxn];
char name[maxn][12];
int nex[maxn];

void init()
{
    for(int i = 1; i <= 500000; i++)
        for(int j = i; j <= 500000; j += i)
            cnt[j]++;
    return;
}

int main()
{
    int n, k;
    init();
    while(~scanf("%d %d", &n, &k))
    {
        int id = 1;
        for(int i = 1; i <= n; i++)
            if(cnt[i] > cnt[id])
                id = i;
        for(int i = 1; i <= n; i++)
            scanf("%s %d", name[i], &nex[i]);
        ST.build(1, n, 1);
        int sum = n;
        int pos = k;
        for(int i = 1; i < id; i++)
        {
            ST.update(1, n, 1, pos, 0);
            sum--;
            if(nex[pos] > 0)
            {
                k = (k - 1 + nex[pos]) % sum;
                if(k == 0) k = sum;
            }
            else
            {
                k = (k - 1 + (sum - (-nex[pos] - 1) % sum)) % sum;
                if(k == 0) k = sum;
            }
            pos = ST.query(1, n, 1, k);
        }
        printf("%s %d\n", name[pos], cnt[id]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值