Poj 2886 Who Gets the Most Candies?

一个简单的线段树上二分+反素数

线段树上二分其实就是单点更新单点查询

具体的每一个节点保存他的所表示的区间内剩余的未出对的人数

然后给出一个人在队伍中的位置的话可以类似于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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值