poj 2886 Who Gets the Most Candies?(线段树单点更新模拟约瑟夫环)

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


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

思路:乍一看这个题真不知道用线段树怎么写,类似于约瑟夫环问题,每次从圈中删除一个人,直到所有人出圈。其实线段树恰好可以解决从圈中删除一人这个问题,即单点更新。

因为n个小孩依次出圈,要知道谁的糖果最多,也就是求谁出圈的次序号的约数最多。所以我们可以预处理,先求出1~n中约数最多的那个数maxid,其约数个数是maxnum。所以我们直接模拟约瑟夫环,谁的出圈序号是maxid,谁得到的糖果最多。

当第一个人出圈后,下一个怎么求?这就是由相对位置求原始位置的问题。只有先求出这次出圈的人的原始位置才能求出下一个人。这是通过线段树来求的。线段树附加区域存的是这个区间还有多少人,每踢出一个人区间的长度减1.求原始位置的方法与poj2828插队问题类似。只不过那个是求出原始位置后直接插入,而这个是返回原始位置。


#include <stdio.h>
#include <string.h>

const int maxn = 500005;

struct line
{
	int l;
	int r;
	int len;
}tree[maxn<<2];

int n,k;
int maxid;	//约束最大的编号
int maxnum;	//约束个数的最大值,即maxid的约束个数
int div[maxn];//预处理每个数的约束个数

//预处理约数个数,并找出约束个数最多的编号maxid及其约束个数maxnum
void solve_division()
{
	memset(div,0,sizeof(div));
	for(int i = 1; i <= n; i++)
	{
		div[i]++;
		for(int j = i*2; j <= n; j+=i)
			div[j]++;
	}
	maxid = 1;
	maxnum = div[1];
	for(int i = 2; i <= n; i++)
	{
		if(div[i] > maxnum)
		{
			maxnum = div[i];
			maxid = i;
		}
	}
}

void build(int v, int l, int r)
{
	tree[v].l = l;
	tree[v].r = r;
	tree[v].len = r-l+1;
	if(l == r)
		return ;
	int mid = (l+r)>>1;
	build(v*2,l,mid);
	build(v*2+1,mid+1,r);
}

int query(int v, int k)	//由相对位置找到其实际位置
{
	tree[v].len--;
	if(tree[v].l == tree[v].r)
		return tree[v].l;

	if(k <= tree[v*2].len)
		return query(v*2,k);
	else return query(v*2+1,k-tree[v*2].len);
}

char name[maxn][15];
int card[maxn];

int main()
{
	while(~scanf("%d %d",&n,&k))
	{
		solve_division();
		for(int i = 1; i <= n; i++)
			scanf("%s %d",name[i],&card[i]);

		build(1,1,n);

		int pos;
		for(int i = 0; i < maxid; i++)	//出圈maxid即可,就能找出第maxid次出圈的人的实际位置
		{
			pos = query(1,k);//找到原始位置
			n--;//出圈,个数减1
			if(n==0) break;
			if(card[pos] > 0)
				k=(k-1+card[pos]-1)%n+1;
			else
				k=((k-1+card[pos])%n+n)%n+1;
		}
		printf("%s %d\n",name[pos],maxnum);
	}
	return 0;
}


你可能感兴趣的:(线段树)