题解思路:
这里我们可以将逆时针一同转化为顺时针,那么就是如果逆时针跳过m个人那么顺时针应该是k(圈里现有人数)-m+1,为什么要加1呢,因为我们出发点是上一次被淘汰的那个人,他已经不属于这个圈子了,所以转化成顺时针要多加1,那么我们假设刚刚被淘汰的人在po位置,那么无非是两种情况:下个被淘汰的在po-n中或1-po中,如果po-n中的人数不够的话那肯定就在1-po中,这个可以用树状数组维护,另外用二分找最近一个等于跳过次数的位置。
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int mx = 5e5+10;
int n,m,k,top;
int cnt[mx],pos[mx],num[mx],sum[mx];
bool vis[mx];
char str[mx][15];
void init()
{
pos[1] = 1;
for(int i=1;i<mx;i++){
cnt[i]++;
for(int j=2*i;j<mx;j+=i)
cnt[j]++;
}
for(int i=2;i<mx;i++)
pos[i] = cnt[pos[i-1]]>=cnt[i]? pos[i-1]:i;
}
inline int lowbit(int x) { return x&(-x); }
void add(int x,int v){ for(int i=x;i<=n;i+=lowbit(i)) sum[i] += v; }
int get_sum(int x)
{
int ans = 0;
while(x){
ans += sum[x];
x -= lowbit(x);
}
return ans;
}
int get_pos(int v,int l,int r)
{
int mid;
while(l<r){
mid = (l+r)>>1;
if(get_sum(mid)>=v) r = mid;
else l = mid+1;
}
return l;
}
int main()
{
init();
while(~scanf("%d%d",&n,&m)){
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++) scanf("%s%d",str[i],num+i),add(i,1);
int t = pos[n],po = 1, ans;
k = n;
while(t--){
int now = get_sum(po-1),eng = get_sum(n);
if(eng-now>=m) ans = get_pos(now+m,po,n);
else ans = get_pos(m-eng+now,1,po-1);
k--;
if(!k) break;
m = num[ans]%k;
if(!m){
if(num[ans]>0) m = k;
else m = 1;
}else if(m<0) m = k+m+1;
po = ans, add(ans,-1);
}
printf("%s %d\n",str[ans],cnt[pos[n]]);
}
return 0;
}