该题和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;
}