题意
传送门 POJ 2886
题解
仿埃氏筛法打 因数表,对于每个质因子,对当前处理的合数的因数数量乘以该质因子数加一。
剔除某个元素后,为确定当前剔除对象,需遍历元素检查是否剔除,复杂度 O ( n 2 ) O(n^2) O(n2)。考虑用树状数组维护当前数列的序号,对于每个序号,二分即可求得此序号对应的元素存储的索引值,复杂度 O ( n l o g n l o g n ) O(nlognlogn) 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;
}