POJ 2886 Who Gets the Most Candies?(线段树模拟约瑟夫环,高合成数)

POJ 2886 Who Gets the Most Candies?(线段树模拟约瑟夫环,高合成数)

ACM

题目地址:POJ 2886 Who Gets the Most Candies?

题意: 
N 个小孩围成一圈,他们被顺时针编号为 1 到 N。每个小孩手中有一个卡片,上面有一个非 0 的数字,游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,他卡片上的数字 A 表明了下一个离开的小孩,如果 A 是大于 0 的,则下个离开的是左手边第 A 个,如果是小于 0 的, 则是右手边的第 -A 个小孩。游戏将直到所有小孩都离开,在游戏中,第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。

分析

  1. 线段树模拟约瑟夫环。第k个小孩根据手牌叫下一个小孩的时候,可以计算出那个是从0开始的第几个,然后就转化为排队问题了。
  2. 然后就是问谁最多的糖果了,其实就是问前n个数中的高合成数。可以先预处理高合成数,或者直接暴力处理每个数的因子个数。

代码

/*
*  Author:      illuz <iilluzen[at]gmail.com>
*  Blog:        http://blog.csdn.net/hcbbt
*  File:        2886.cpp
*  Create Date: 2014-08-06 14:10:51
*  Descripton:  segment tree 
*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define repf(i,a,b) for(int i=(a);i<=(b);i++)

#define lson(x) ((x) << 1)
#define rson(x) ((x) << 1 | 1)

typedef long long ll;

const int N = 500100;
const int ROOT = 1;

int n, k;
int maxid, maxnum;
int tab[N];
char name[N][15];
int card[N];

// below is sement point updated version
struct seg {
	ll w;
};

struct segment_tree { 
	seg node[N << 2];

	void update(int pos) {
		node[pos].w = node[lson(pos)].w + node[rson(pos)].w;
	}

	void build(int l, int r, int pos) {
		if (l == r) {
			node[pos].w = 1;
			return;
		}
		int m = (l + r) >> 1;
		build(l, m, lson(pos));
		build(m + 1, r, rson(pos));
		update(pos);
	}

	// remove the point that the sum of [0, it] is x, return its id
	int remove(int l, int r, int pos, ll x) {
		if (l == r) {
			node[pos].w = 0;
			return l;
		}
		int m = (l + r) >> 1;
		int res;
		if (x <= node[lson(pos)].w)
			res = remove(l, m, lson(pos), x);
		else
			res = remove(m + 1, r, rson(pos), x - node[lson(pos)].w);
		update(pos);
		return res;
	}
} sgm;

// record the num of division, O(nlogn)
void pre() {
	repf (i, 1, N - 1) {
		for (int j = i; j < N; j += i)
			tab[j]++;
	}
}

void getmaxnum(int n) {
	maxid = 1;
	maxnum = tab[1];
	repf (i, 2, n) {
		if (tab[i] > maxnum) {
			maxnum = tab[i];
			maxid = i;
		}
	}
}

int main() {
	pre();
	while (~scanf("%d%d", &n, &k)) {
		getmaxnum(n);
		sgm.build(1, n, ROOT);
		repf (i, 1, n) {
			scanf("%s%d", name[i], &card[i]);
		}
		int pos, rest = n;
		repf (i, 1, maxid) {
			pos = sgm.remove(1, n, ROOT, k);
			if (--rest == 0)
				break;
			if (card[pos] > 0)
				k = (k - 1 + card[pos] - 1) % rest + 1;
			else
				k = ((k - 1 + card[pos]) % rest + rest) % rest + 1;
		}
		printf("%s %d\n", name[pos], maxnum);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值