线段树(3) poj2886

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

这道题的难点不在于线段树,而在于反素数和下一个人位置的确定。反素数我准备用一个博客写。这里先不讲。知道了反素数和下一个人的位置,这道题就转变为求第k大数。我记得求第K大数有很多题了, 我的博客就写了4-5题了。由于存在删除所以要采用线段树或树状数组。先写树状数组,再写线段树,今天又把他们求第k数的思想想了一下。

树状数组版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int n, k;
const int MAX = 500000 + 10;
int c[MAX];
struct node
{
	char name[20];
	int card;
}child[MAX];
int a[37]={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,500001};  
int b[37]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,
			100,108,120,128,144,160,168,180,192,200,1314521};
			
int lowbit(int x)
{
	return x&(-x);
}
void update(int x, int v)
{
	while (x<=n)
	{
		c[x] += v;
		x +=lowbit(x);
	}
}
int getsum(int x)
{
	int sum = 0;
	while (x)
	{
		sum += c[x];
		x -= lowbit(x);
	}
	return sum;
}
int find(int x)
{
	int l = 1, r = n, mid;
	while (l < r)
	{
		mid = (l +r)>>1;
		if (getsum(mid)<x)
		{
			l = mid + 1;
		}
		else
		{
			r = mid;
		}
	}
	return l;  
}
int main()
{
	int i, j;
	while (scanf("%d %d", &n, &k) == 2)
	{
		memset(c, 0, sizeof(c));
		for (i=1; i<=n; i++)
		{
			update(i, 1);
			scanf("%s%d", child[i].name, &child[i].card);
		}
		i=0;
		while (a[i]<=n)
		{
			i++;
		}
		i--;
		int pos = 0, t = n;
		for (j=0; j<a[i]; j++)
		{
			t--;
		
			pos = find(k);
			if (j==a[i]-1)
			{
				break;
			}
			update(pos, -1);
			if (child[pos].card>0)
			{
				k = (k + child[pos].card-1) % t;
				if (k ==0)
				{
					k = t;
				}
			}
			else
			{
				k = ((k + child[pos].card)%t+t) % t;
				if (k == 0)
				{
					k = t;
				}
			}
			
		}
		printf("%s %d\n", child[pos].name, b[i]);
	}
}


线段树版本:

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

const int MAX = 500000 + 10;
char name[MAX][11];
int card[MAX];
int c[37]={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,500001};  
int b[37]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,
			100,108,120,128,144,160,168,180,192,200,1314521};
struct node
{
	int l;
	int r;
	int sum;
}a[MAX<<2];

void pushup(int pos)
{
	a[pos].sum = a[pos<<1].sum + a[pos<<1|1].sum;
}

void build(int l, int r, int pos)
{
	a[pos].l = l;
	a[pos].r = r;
	if (l == r)
	{
		a[pos].sum = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, pos<<1);
	build(mid+1, r, pos<<1|1);
	pushup(pos);
}
int query(int key, int pos)
{
	a[pos].sum--;
	if (a[pos].l == a[pos].r)
	{
		return a[pos].r;
	}
	if (key<=a[pos<<1].sum)
	{
		return query(key, pos<<1);
	}
	else
	{
		return query(key - a[pos<<1].sum, pos<<1|1);
	}
}
int main()
{
	int n, k, i, ansscore, anspos, pos;
	while (scanf("%d%d", &n, &k) == 2)
	{
		i = 0;
		while (c[i] <= n)
		{
			i++;
		}
		anspos = c[i-1];
		ansscore = b[i-1];
		build(1, n, 1);
		for (i=1; i<=n; i++)
		{
			scanf("%s%d", name[i], &card[i]);
		}
		for (i=1; i<=anspos; i++)
		{
			n--;
			pos = query(k, 1);
			if (i == anspos)
			{
				break;
			}
			if (card[pos]>0)
			{
				k = (k + card[pos]-1) % n;
				if (k ==0)
				{
					k = n;
				}
			}
			else
			{
				k = (k + card[pos]%n+n) % n;
				if (k == 0)
				{
					k = n;
				}
			}
		}
		printf("%s %d\n", name[pos], ansscore);
	}
}


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值