需要用到反素数表,先打个表。首先最大值是可以事先求出来。然后开始模拟,每次利用树状数组+二分确定当前出来的人的实际位置,其相对于起始的位置可以直接求。
确定实际位置时二分枚举可能位置,如果其相对位置与前面已经出去的人数之和 与当前枚举位置相等 并且当前枚举位置未被占用则为其实际位置。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<string.h>
using namespace std;
const int nMax=500005;
int cntrp[nMax],rp[nMax];
int p[10]={2,3,5,7,11,13,17};
int N,K,a[nMax],c[nMax],vis[nMax];
char name[nMax][15];
void rprime(int k,int lim,int pc,int cur,int x)
{
if(k>=7) return;
if(cntrp[x]<pc)
{
cntrp[x]=pc;rp[x]=cur;
}
else if(cntrp[x]==pc && cur<rp[x]) rp[x]=cur;
for(int i=1;i<=lim;i++)
{
long long t=pow(p[k],i*1.0);
if(t*cur<=x)
rprime(k+1,i,pc*(i+1),t*cur,x);
}
}
void init()
{
memset(cntrp,0,sizeof(cntrp));
for(int i=nMax;i>=1;)
{
rprime(0,20,1,1,i);
int k=rp[i],c=cntrp[i];
while(i>=k)
rp[i]=k,cntrp[i--]=c;
}
}
int lowbit(int x)
{
return x&(-x);
}
void insert(int x,int detal)
{
for(int i=x;i<=N;i+=lowbit(i))
c[i]+=detal;
}
int getsum(int x)
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
sum+=c[i];
return sum;
}
int slove(int pos)
{
int l=1,r=N,res;
while(l<=r)
{
int mid=(l+r)>>1;
int t=getsum(mid-1)+pos;
if(t==mid)
{
if(!vis[mid]) res=mid,r=mid-1;
else l=mid+1;
}
else if(t>mid) l=mid+1;
else r=mid-1;
}
return res;
}
int main()
{
// freopen("test.txt","r",stdin);
init();
while(scanf("%d%d",&N,&K)!=EOF)
{
for(int i=1;i<=N;i++)
scanf("%s %d",name[i],&a[i]);
int pos=K;
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
for(int k=1;k<=N;k++)
{
int cur=slove(pos);
if(k==rp[N])
{
printf("%s %d\n",name[cur],cntrp[N]);
break;
}
insert(cur,1);vis[cur]=1;
if(a[cur]>0)
{
pos=(a[cur]+pos-1)%(N-k);
if(pos<=0) pos=N-k;
}
else
{
pos=(pos+a[cur])%(N-k);
if(pos<=0) pos+=(N-k);
}
}
}
return 0;
}