POJ 2886 Who Gets the Most Candies?(线段树 + 约瑟夫环 + 反素数)

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

题目连接

题意 : N个人顺时针围成一圈,每个人有一张卡片,轮到某个人的时候进行以下操作 :1 , 这个人退出这个圈。2,如果这个人的卡片上的数字是正数X则向右数X个位置,otherwise向左数(-X)个位置。3被数到的那个人执行相同操作,直至没有人为止。4,如果原来的那个人是第K个,那么他将会获得P(K)颗糖。 注 : P(k)表示k的不同约数数量(可以是1或者本身)。

思路 : 首先给定一个数N的话可以利用反素数打表很快求出到底是第M个执行操作获得的Candies最多。 然后约瑟夫环模拟寻找第M个人是谁。另外可以用线段树保存绝对位置下标和执行寻找相对位置下标这一操作。

注意 :是卡片数字是正的往右数喔 。


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 500005;
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
int max(int a, int b){return a > b ? a : b;}

int ans[maxn][2];

char name[maxn][12];
int cov[maxn<<2], aa[maxn], n, k;
int inprime[40] = {
1, 2, 4, 6, 12, 24, 36, 48, 60,120, 180, 240, 360, 720, 840, 1260, 1680, 2520, 5040,7560, 10080, 15120, 
20160, 25200,27720, 45360, 50400, 55440, 83160,110880, 166320, 221760, 277200, 332640, 498960
}, MaxSize = 35, fg[40];


void pushup(int rt)
{
	cov[rt] = cov[rt<<1] + cov[rt<<1|1];
}

void build(int rt, int l, int r)
{
	if (l == r)
	{
		cov[rt] = 1;
		return ;
	}
	int mid = (l + r) >> 1;
	build(lson); build(rson);
	pushup(rt);
}

void update(int rt, int l, int r, int pos)
{
	if (l == r)
	{
		cov[rt] = 0;
		return ;
	}
	int mid = (l + r) >> 1;
	int lv = cov[rt<<1];
	if (pos <= lv)update(lson, pos);
	else update(rson, pos-lv);
	pushup(rt);
}

int query(int rt, int l, int r, int pos)
{
	if (l == r)
	{
		return l;
	}
	int mid = (l + r) >> 1, lv = cov[rt<<1];
	if (pos <= lv)return query(lson, pos);
	else return query(rson, pos-lv);
}

int ysf(int sx, int Mx, int num)
{
	if (num == 2)return 1;
	int res;
	if (Mx <= 0)
	{
		Mx = -Mx;
		if (Mx < sx)return sx - Mx;
		if (Mx == sx)return num - 1;
		Mx -= (sx-1);
		res = (Mx - 1) % (num - 1) + 1;
		return num - res;
	}
	else
	{
		res = Mx - (num - sx);
		return (res + num - 2) % (num - 1) + 1;
	}
	
}

void solve()
{
	build(1, 1, n);
	int kk = k, e = k, m = n;
	for (int i = 2; i <= ans[n][0]; i++)
	{
		update(1, 1, n, kk);
		kk = ysf(kk, aa[e], m);
		e = query(1, 1, n, kk);
		m--;
	}
	printf("%s %d\n", name[e], ans[n][1]);
}


int get(int x)
{
	int cnt = 0;
	for (int i = 1; i*i <= x; i++)if (x % i == 0)
	{
		cnt++;
		if (x/i != i)cnt++;
	}
	return cnt;
}

void init()
{
	int i;
	memset(ans, 0, sizeof(ans));
	for (i = 0; i < MaxSize; i++)
	{
		fg[i] = get(inprime[i]);
		ans[inprime[i]][0] = inprime[i];
		ans[inprime[i]][1] = fg[i];
	}
	int Maxfg = 0, Maxv = 0;
	for (i = 1; i <= maxn-1; i++)
	{
		Maxv = ans[i][0] = max(ans[i][0], Maxv);
		Maxfg = ans[i][1] = max(ans[i][1], Maxfg);
	}
}

int main()
{
	int i;
	init();
	while (scanf("%d%d", &n, &k) == 2)
	{
		for (i = 1; i <= n; i++)
			scanf("%s%d", name[i], &aa[i]);
		solve();
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值