题意:n个人围城一圈,每个人决定下一个出局的人在他的第几个位置,首先出局的人是第k个人
分析:反素数+约瑟夫
这道题最主要需要理解的就是线段树是如何模拟的反素数,sum数组记录的是队列中还剩余多少个人
k表示的是在剩余里的人,其排在第几个,通过对线段树的询问找到该第k个人在初始队列中排第几个
若k<=sum[rt<<1]则进入到其左子节点,否则的话,k-sum[rt<<1|1]进入到右子节点
还有一个难点就是,剩余中k位置的更新,当next[id]>0的时候,在k-1位置的人已经被删除了,所以应该
在加上next[id]后还应该减去1,同时每次取余的时候t都已经减1了,k在加上1表示的第几个人
next[id]<0
k = ((k-1+next[id])%t+t)%t+1;
next[id]>=0
k = (k-1+next[id]-1)%t+1;
#include<stdio.h> #include<string.h> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 const int MN= 500100; int sum[MN<<2]; int next[MN]; char nam[MN][20]; int a[37]={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,500001}; int b[37]={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,1314521}; void PushUP(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt) { if(l==r) { sum[rt]=1; return ; } int m=(l+r)>>1; build(lson); build(rson); PushUP(rt); } int query(int k,int l,int r,int rt) { if(l==r) { sum[rt]--; return l; } int m=(l+r)>>1; int ret; if(k<=sum[rt<<1]) ret=query(k,lson); else { k-=sum[rt<<1]; ret=query(k,rson); } PushUP(rt); return ret; } int main() { int i,n,k; while(scanf("%d%d",&n,&k)!=EOF) { for(i=0;i<n;i++) scanf("%s%d",nam[i],&next[i]); build(0,n-1,1); i=0; while(a[i]<=n) i++; int p=a[i-1]; int MAX=b[i-1]; int id; int t=n; for(i=0;i<p;i++) { t--; id=query(k,0,n-1,1); //id表示的是初始的位置在哪 if(t==0) break; if(next[id]<0)//向前 { k = ((k-1+next[id])%t+t)%t+1; } else//向后 {//k表示的剩余人中的第几个 k = (k-1+next[id]-1)%t+1; } } printf("%s %d\n",nam[id],MAX); } return 0; }