题目链接:点我啊╭(╯^╰)╮
题目大意:
有 n 个人,第一次选择第 k 个人退出,每个人都有一个整数值 k,整数代表向左数第k个人退出,负数则向右数
每个人在退出时,会得到一定数量的糖果,若则个人是第 m 个退出的,则可以得到 m 的因数 个糖果,问得到最大糖果数量的那个孩子是谁??
解题思路:
反素数问题,什么是反素数可以自行百度,问题就转化为了求最接近n的反素数,反素数可以暴力求出和或者打表,下面的代码给出了暴力的过程
这题同时由于给出了顺序,所以这个退出的过程就必须模拟
那么问题就在于怎么处理这个退出的过程了,要实现 log(n) 的时间,这里引入线段树的单点更新,本题和POJ 2828很相似,可以点进去看一下
代码思路:
和POJ 2828一样处理好单点更新的过程,还有暴力打表的过程,对正负数的处理可以用模处理,看代码即可
核心:线段树的好题,同时了解到反素数的好处
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pushup(rt) t[rt] = t[rt<<1] + t[rt<<1|1];
const int maxn = 500000+10;
ll t[maxn<<2], a[maxn][5], ans[maxn], id, n;
struct Node {
char name[20];
int val;
} boy[maxn];
void build(int l,int r,int rt) {
if(l==r) {
t[rt] = 1;
return ;
}
int m = (l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
int update(int pos,int l,int r,int rt) {
if(l==r) {
t[rt]--;
return l;
}
int m = (l+r)>>1, ret;
if(pos<=t[rt<<1]) ret = update(pos,lson);
else ret = update(pos-t[rt<<1],rson);
pushup(rt);
return ret;
}
void init() {
memset(ans, 0, sizeof(ans));
for(int i=1; i<=n; i++) {
ans[i]++;
for(int j=2*i; j<=n; j+=i)
ans[j]++;
}
id = 1;
int maxx = ans[1];
for(int i=2; i<=n; i++) //找出第几个人跳出获得的糖最多
if(ans[i]>maxx) {
maxx=ans[i];
id=i;
}
}
void printf_ans(int l,int r,int rt){
if(l==r){
printf("%d ", t[l]);
return ;
}
int m = (l+r)>>1;
printf_ans(lson);
printf_ans(rson);
}
int main() {
int k;
while(~scanf("%d%d", &n, &k)) {
build(1,n,1);
init();
for(int i=1; i<=n; i++) scanf("%s %d", boy[i].name, &boy[i].val);
int pos=0, mod=t[1];
boy[0].val=0;
int ans_id = id;
while(id--) {
if(boy[pos].val>0) //k表剩余的人中从左起第k中出队
k=((k-1+boy[pos].val-1) % mod+mod) % mod+1;
else
k=((k-1+boy[pos].val) % mod+mod) % mod+1;
pos = update(k,1,n,1);
mod=t[1];
}
printf("%s %d\n", boy[pos].name, ans[ans_id]);
}
}