POJ 2886: Who Gets the Most Candies?

POJ100問AC記念.

概要

N人の子供が一枚ずつカードを持っていて,輪になって並んでいる.
K番目の子供からスタートする.まず,現在選んでいる子供を輪から除外する.除外した子供が持っていたカードに書かれている番号だけずれた子供を選ぶ.これを繰り返す.
i番目の操作で除外された子供は,iの約数の数だけキャンディをもらえる.
一番多くキャンディをもらえる子供は誰か.

解法

約数の個数は篩みたいにするとうまく計算できる.
子供を除外してずれる番号は,BITをうまく利用して管理する.
次に選ぶ子供はBIT上で二分探索をして求めることができる.

注意

同じ個数を得る子供は,先に輪の外に出た方を出力.
カードの番号が負の時の扱いに注意

const int MN = 500010;

int N, K;
int A[500010];
char name[500010][16];
int bit[500010];
bool isPrime[500010];
ll F[500010];

void update(int i, int v){
	++i;
	while(i <= N){
		bit[i] += v;
		i += i & -i;
	}
}
int get(int i){
	++i;
	int res = 0;
	while(i>0){
		res += bit[i];
		i -= i & -i;
	}
	return res;
}
int find(int num){
	int lb = -1, ub = N;
	while(ub - lb > 1){
		int mid = (ub + lb) / 2;
		if(get(mid)-1 < num){
			lb = mid;
		}
		else{
			ub = mid;
		}
	}
	return ub;
}
void solve(){
	memset(bit, 0, sizeof(bit));
	for(int i=0;i<N;++i)update(i,1);
	int mx = 0, ridx = 0, idx = --K;

	for(int k=0;k<N;++k){
		if(F[k+1] > mx){
			mx = F[k+1];
			ridx = idx;
		}
		update(idx, -1);
		if(k == N - 1) break;
		int num = N - k - 1;
		if(A[idx] > 0){
			K = (K + A[idx] - 1) % num;
		}else{
			K = (K + A[idx]) % num;
		}
		if(K < 0) K += num;
		idx = find(K);
	}
	printf("%s %d\n", name[ridx], mx);
}
main(){
    for(int i = 0; i < MN; ++i) F[i] = 1;
    for(int i = 2; i < MN; ++i) if(!isPrime[i]){
		for(int j = i; j < MN; j += i){
			int k = 0;
			for(int l=j; l % i == 0; l /= i, ++k);
			isPrime[j] = true;
			F[j] *= k + 1;
		}
    }
	while(~scanf("%d%d", &N, &K)){
		for(int i=0;i<N;++i){
			scanf("%s%d", name+i, A+i);
		}
		solve();
	}
	return 0;
}