一个简单的线段树上二分+反素数
线段树上二分其实就是单点更新单点查询
具体的每一个节点保存他的所表示的区间内剩余的未出对的人数
然后给出一个人在队伍中的位置的话可以类似于splay往下找点的姿势去找到位置所对应的绝对位置(其实也就是这个节点在原区间所对应的位置
还有需要注意的是在删除一个点之后往前走和往后走是需要分开处理的,考虑这个点后面的点的位置都往后挪了一位
然后反素数只是打表而已,感觉没什么
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> using namespace std; #define root 1,1,n #define lson o<<1,l,m #define rson o<<1|1,m+1,r #define Mid int m = l + (r - l) /2 #define Now int o,int l,int r const int maxn = 562345; int arr[maxn*4]; void update(Now,int pos,int val){ if(l == r){ arr[o] = val; return; } Mid; if(pos <= m){ update(lson,pos,val); } else{ update(rson,pos,val); } arr[o] = arr[o<<1] + arr[o<<1|1]; } int query(Now,int id){ if(l==r){ return l; } Mid; if(arr[o<<1] >= id){ return query(lson,id); } else{ return query(rson,id-arr[o<<1]); } } char name[maxn][30]; int order[maxn]; int offset[maxn]; int hug[40]={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 frac[40]={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 poser[maxn]; int cal(int x){ return frac[lower_bound(hug,hug+36,x)-hug]; } int main(){ int n,k; memset(poser,0,sizeof(poser)); for(int i=0;i<36;i++){ poser[hug[i]]=hug[i]; } for(int i=1;i<500100;i++){ if(!poser[i]) poser[i] = poser[i-1]; } while(~scanf("%d %d",&n,&k)){ for(int i=1;i<=n;i++){ scanf("%s",name[i]); scanf("%d",&offset[i]); } for(int i=1;i<=n;i++){ update(root,i,1); } int left = n; int lenth=0; k--; while(left>0){ int id = query(root,k+1); update(root,id,0); order[lenth++]=id; left--; if(offset[id]>0) <span style="white-space:pre"> </span>k--; if(left) k = ((k+offset[id])%left+left) % left; } int ans = order[poser[n]-1]; printf("%s",name[ans]); printf(" %d\n",cal(poser[n])); } return 0; }