poj 2886 - 树状数组+二分

题解思路:

这里我们可以将逆时针一同转化为顺时针,那么就是如果逆时针跳过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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值