【POJ 2886】Who Gets the Most Candies?

时间:2023-03-08 19:22:59

约瑟夫问题的升级版,每次出去的是前一个出去的人位置+手上的数字(正往前,负往后)。第i个出去的人拿的糖是i的约数的个数。求拿糖最多的人和他的糖果数。

分析

线段树单点更新,反素数。

我竟然WA在了反素数少了几个QAQ

代码

#include<cstdio>
#include<cstring>
#define N 500002
#define mid int m=l+((r-l)>>1)
#define lson l,m,node<<1
#define rson m+1,r,node<<1|1
using namespace std; int v[N];
char name[N][];
int n,k;
int sum[N<<]; int rprim[]= {,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,};//反素数
int nprim[]= {,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,};//反素数的约数个数 void build(int l,int r,int node)
{
sum[node]=r-l+;//sum储存这个区间有多少个数
if(l<r)
{
mid;
build(lson);
build(rson);
}
}
int out(int l,int r,int node,int k)
{
sum[node]--;//这个区间减少一个数
if(l==r)
return l;//返回这个减少的数的原始下标
mid;
if(k<=sum[node<<])//要找的第k个数小于等于左半区间的个数
return out(lson,k);//就递归左子树
else
return out(rson,k-sum[node<<]);//否则就在右子树,且k-左子树的个数
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
memset(name,,sizeof(name));//清空名字
for(int i=; i<=n; i++)
scanf("%s%d",name[i],&v[i]);
build(,n,); int tn=n,now,p=;
while(rprim[p]<=n)p++;//找出n里最大的反素数 for(int i=; i<rprim[p-]; i++)//反素数前的都出队
{
now=out(,n,,k);//当前出队的序号
tn--;//剩下的人数
if (v[now]>)//向前数
k=(k-+v[now])%tn;//先减去本身这个位置 然后往前v个 再取模
else
k=((k+v[now])%tn+tn)%tn;//直接往后 然后要取模再取模保证正数
if (k==) k=tn;//如果刚好是tn 取模会变成0
}
now=out(,n,,k);//得到第最大的反素数个出队的人的序号
printf("%s %d\n",name[now],nprim[p-]);
}
return ;
}