poj 2886 - 树状数组+二分

题解思路:

这里我们可以将逆时针一同转化为顺时针,那么就是如果逆时针跳过m个人那么顺时针应该是k(圈里现有人数)-m+1,为什么要加1呢,因为我们出发点是上一次被淘汰的那个人,他已经不属于这个圈子了,所以转化成顺时针要多加1,那么我们假设刚刚被淘汰的人在po位置,那么无非是两种情况:下个被淘汰的在po-n中或1-po中,如果po-n中的人数不够的话那肯定就在1-po中,这个可以用树状数组维护,另外用二分找最近一个等于跳过次数的位置。


代码:

#include
#include
#include
#include
#include
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=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>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;
}


你可能感兴趣的:(树状数组,二分系列)