POJ 2886 BIT + 二分

题意

传送门 POJ 2886

题解

仿埃氏筛法打 因数表,对于每个质因子,对当前处理的合数的因数数量乘以该质因子数加一。

剔除某个元素后,为确定当前剔除对象,需遍历元素检查是否剔除,复杂度 O ( n 2 ) O(n^2) 。考虑用树状数组维护当前数列的序号,对于每个序号,二分即可求得此序号对应的元素存储的索引值,复杂度 O ( n l o g n l o g n ) O(nlognlogn)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#define abs(x)    ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-5
#define PI 3.14159265358979323846
#define MAX_N 500005
#define MAX_C 15
using namespace std;
int N, K;
int num[MAX_N], factor[MAX_N];
char name[MAX_N][MAX_C];
int bit[MAX_N];
// BIT
int sum(int i){
	int s = 0;
	while(i > 0){
		s += bit[i];
		i -= i & -i;
	}
	return s;
}

void add(int i, int x){
	while(i <= N){
		bit[i] += x;
		i += i & -i;
	}
}
// 打因数表
void sieve(){
	fill(factor, factor + MAX_N, 1);
	for(int i = 2; i < MAX_N; i++){
		if(factor[i] == 1){
			factor[i] = 2;
			for(int j = i * 2; j < MAX_N; j += i){
				int t = j, n = 1;
				while(t % i == 0) t /= i, ++n;
				factor[j] *= n;
			}
		}
	}
}
// 二分求当前数列标号对应人的下标
int get(int k){
	int lb = 0, ub = N;
	while(ub - lb > 1){
		int mid = (lb + ub) >> 1;
		if(sum(mid) < k) lb = mid;
		else ub = mid;
	}
	return ub;
}

int main(){
	sieve();
	while(~scanf("%d%d", &N, &K)){
		memset(bit, 0, sizeof(bit));
		for(int i = 1; i <= N; i++){
			scanf(" %s%d", name + i, num + i);
			add(i, 1);
		}
		int res = -1, ri, k = K, mod = N;
		for(int t = 1; t <= N; t++){
			int id = get(k);
			if(res == -1 || factor[t] > factor[res]) res = t, ri = id;
			// 剔除的处理
			add(id, -1);
			if(--mod == 0) break;
			// 更新要剔除的序列标号
			k = (k + (num[id] < 0 ? num[id] : num[id] - 1)) % mod;
			if(k < 0) k += mod;
			else if(k == 0) k = mod;
		}
		printf("%s %d\n", name[ri], factor[res]);
	}
	return 0;
}
发布了91 篇原创文章 · 获赞 1 · 访问量 1590

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/105308206
今日推荐