其中用到了反素数参考http://baike.baidu.com/view/2621997.htm
思路就是给定一个确定的N,约数最大的P是确定的就是不大于N的最大反素数,所以可以打表
然后用一个线段树记录空位模拟出队的过程,找出第P次出队人的编号。线段树的区间存的为编号1~N的人在队
中的人数,update(t,val)就是更新线段树让第val个立刻,并求出他的编号。
当出队人对应的card值为正时,下一个val就算 (val-1+card-1)%p+1 p为此人出去后盛的人数。
当card为负 val为(val-1+card-1)%p+1,
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#define MAX 500010
#define L(x) (x<<1)
#define R(x) (x<<1|1)
using namespace std;
int RPrime[]={//反素数
1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120,
20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960,
554400
};
int fact[]={//反素数约数个数
1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,90,96,100,108,120,128,
144,160,168,180,192,200,216
};
int bsearch(int l,int r,int n)
{
int m=(l+r)/2;
if(r-l==1||r==l)
return l;
if(RPrime[m]==n)return m;
if(RPrime[m]>n)
{
return bsearch(l,m,n);
}
else
return bsearch(m,r,n);
}
struct node
{
int l,r,sum;
}a[MAX*3];
char name[MAX][10];
int card[MAX];
void build(int t,int l,int r)
{
a[t].l=l;
a[t].r=r;
a[t].sum=0;
if(l==r)
{
a[t].sum=1;
return;
}
int mid=(l+r)/2;
build(L(t),l,mid);
build(R(t),mid+1,r);
a[t].sum=a[L(t)].sum+a[R(t)].sum;
}
int findp=0;
void update(int t,int val)
{
if(a[t].l==a[t].r)
{
a[t].sum=0;
findp=a[t].l;
return ;
}
if(val<=a[L(t)].sum)
update(L(t),val);
else
update(R(t),val-a[L(t)].sum);
a[t].sum=a[L(t)].sum+a[R(t)].sum;
}
int getpos(int p,int k)
{
int pos=(k-1+card[findp-1]-1)%p;
if(card[findp-1]<0)
pos=(k-1+card[findp-1])%p;
if(pos<0)
pos+=p;
return pos+1;
}
int main()
{
int n,k;
int endval;
int p;
int num=0;
int i;
while(scanf("%d%d",&n,&k)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%s",&name[i]);
scanf("%d",&card[i]);
}
num=bsearch(0,35,n);
endval=RPrime[num];
build(1,1,n);
p=n;
findp=0;
update(1,(k)%p);
p--;
for(i=2;i<=endval;i++)
{
k=getpos(p,k);
update(1,k);
p--;
}
printf("%s %d\n",name[findp-1],fact[num]);
}
}