POJ 2886 线段树

该题和poj2828的思路非常类似。

题意:

有N个人参加一个游戏,每个人手上都拿了一张卡片,每张卡片的数字都是非0的,现在从第K人开始,第K人离开后,这时候根据第K人手上拿着的卡片的数字,决定下一个人是谁,如果这个数字m为正数,那么其实相当于向右数m个,如果这个数字为负数,那么其实相当于向左数m个数的位置(所有的数法都是要排除自己)。

现在构建一颗类似于poj2828的线段树,这时候主要的关键是快速的定位下一个数该是哪一个空位。如果这个数是正数y,那么当前位置K前面的数有K-1个,即前面有K-1个数,这时候为防止取模后等于0,先减去1,最后取模完成后再加上1,这时候加上y,就是下一个数的位置(注意这里说的位置说的是第几个空位),但是你看在代码里面可能发现有些与上面的公式不同,因为K-2有可能是负数,但是空位 不可能是负数,所以加上剩下的数的总数已达到正数的目的。然后

接下来说下负数,负数我们可以这样思考,因为正数相当于向前走,那么负数就是说要倒着数,和正数一样,先减去1,然后取模再加上1。

#include<iostream>
#include<fstream>
#include<string>
#include<cmath>
#include<cstring>
using namespace std;
#define Lchild rt<<1,L,m
#define Rchild rt<<1|1,m+1,R
#define maxn 500100
int tree[maxn * 3];
int ans[maxn];
//ofstream fout("fout.txt");
char name[maxn][20];
int inte[maxn];
int N;
void build(int rt=1,int L=1,int R=N)
{
	tree[rt] = R - L + 1;
	if (L == R)
		return;
	int m = (L + R) >> 1;
	build(Lchild);
	build(Rchild);
}
int query(int pos,int rt=1,int L=1,int R=N)
{
	tree[rt]--;
	if (L == R)
	{

		return L;
	}
	int m = (L + R) >> 1;
	int ret;
	if (tree[rt << 1] >= pos)
		ret=query(pos, Lchild);
	else
	{
		pos -= tree[rt << 1];
		ret=query(pos, Rchild);
	}
	return ret;
}
void init()
{
	int s = (int)sqrt(maxn*1.0);
	for (int i = 1; i <= s; i++)
	{
		for (int j = i + 1; j*i <= maxn; j++)
			ans[j*i] += 2;
		ans[i*i]++;
	}
}
int main()
{
	int K;
	init();
	while (scanf_s("%d%d",&N,&K)!=EOF)
	{
		build();
		for (int i = 1; i <= N; i++)
		{
			cin >> name[i];
			scanf_s("%d", &inte[i]);
		}
		int ret;
		int ansstr;
		int ansnum=0;
		int t = 0;
		while (tree[1])
		{
			int pos = K;
			ret = query(pos);
			t++;
			pos = inte[ret];
			if (ans[t] > ansnum)
			{
				ansstr = ret;
				ansnum = ans[t];
			}
			if (!tree[1])
				break;
			if (pos > 0)
			{
				pos = ((K - 2 + tree[1]) % tree[1]+pos)%tree[1]+1;
			}
			else
			{
				pos = ((K + pos - 1) % tree[1]+tree[1]) % tree[1] + 1;
			}
			K = pos;
		}
		cout << name[ansstr]<< " " << ansnum << endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值