题意:有n个小朋友顺时针围着坐了一圈,每个人手里都有一个数字。游戏从第k个小朋友开始,首先,第k个小朋友退出游戏,如果退出游戏的小朋友手上的数字是正整数A,则从他的左手边的第A个小朋友退出游戏,如果退出游戏的小朋友手上的数字是负整数-A,则从他的右手边的第A个小朋友退出游戏(注意,因为顺时针,左手边就是往后,右手边就是往前)。第i个退出的小朋友可以获得a(i的约数的个数)的糖果,问最多糖果的小朋友的名字,并且他有多少糖果。
刚开始看这题题目的时候,哇。。看不懂。。最后求的东西不知道是什么,上网搜了一下大佬的题解,才知道,求的好像是翻素数。。。但是。。。重点在于。。我不会啊。。。所以还是老老实实初始化了一下,然后说这题是用线段树做,一直没懂线段树要怎么用上去,看了题解才明白。。。太菜了太菜了。。
思路:先初始化一下5e5+10每个数字的约数有几个(大佬就直接打表反素数。。),我们可以反过来想,可以用类似于素数打表的那个。接着用线段树去求每个小朋友的位置。这题线段树用的比较特殊(代码注释),在于修改的部分其它都差不多。
反素数:对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反素数。例如,整数1,2,4,6等都是反素数。
AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 5e5+10;
struct people{
char name[10];
int num;
}p[maxn]; //每个小朋友的名字和手上的数字
int prime[maxn],node[maxn<<2]; //prime:记录每个数字的约数。 node:线段树的结点
void init(){ //初始化
int i, j;
memset(prime, 0, sizeof(prime));
for(i = 1; i <= maxn; i++){
prime[i]++;
for(j = i*2; j <= maxn; j+=i)
prime[j]++;
}
}
void build(int l,int r,int rt){ //普通线段树建树
if(l == r){
node[rt] = 1;
return;
}
int m = l+r>>1;
build(l, m, rt<<1);
build(m+1, r, rt<<1|1);
node[rt] = node[rt<<1] + node[rt<<1|1];
}
int update(int pos,int l,int r,int rt){ //pos在剩下来的人中的位置
if(l == r){
node[rt] = 0;
return l;
}
int m = l+r>>1,ans;
if(pos <= node[rt<<1]) //这里node表示此区间的人数,如果pos小的话,所以要找的人在前面一个区间。
ans = update(pos, l, m, rt<<1);
else //如果pos大于前面一个区间的人数,则pos在后面一个区间,但是我们要减去前一个区间的人数。
ans = update(pos - node[rt<<1], m+1, r, rt<<1|1);
node[rt] = node[rt<<1]+node[rt<<1|1];
return ans;
}
int main(){
int n, k, Max, ans, i;
init();
while(scanf("%d%d",&n,&k)!=EOF){
for(i = 1; i <= n; i++){
scanf("%s%d",p[i].name,&p[i].num);
}
build(1, n, 1);
Max = 0,ans = 0;
for(i = 1; i <= n; i++){ //找出前n个数,反素数最大的是哪个并记录下来
if(prime[i] > Max){
ans = i; //记录位置
Max = prime[i]; //记录最多的糖果
}
}
int pos, now = 0, mov;
while(true){
pos = update(k, 1, n, 1);
if(++now == ans) //到达此位置,退出循环
break;
mov = p[pos].num;
if(mov > 0)
k = (k - 1 + mov - 1)%node[1] + 1; //我们现在往后走,但是原本的第k个小朋友已经退出游戏了,所以现在的第k是原来的k+1个,所以我们要先-1。后面的-1为防止出现0。
else
k = ((k - 1 + mov)%node[1]+node[1])%node[1] + 1; //现在往前走,所以第一个-1就不用了。
}
printf("%s %d\n",p[pos].name,Max);
}
return 0;
}