题目链接:点我啊╭(╯^╰)╮
题目大意:
有 n n n 个人,第一次选择第 k k k 个人退出,每个人都有一个整数值 k k k,整数代表向左数第 k k k 个人退出,负数则向右数
每个人在退出时,会得到一定数量的糖果,若则个人是第 m m m 个退出的,则可以得到 m m m 的因数 个糖果,问得到最大糖果数量的那个孩子是谁??
解题思路:
反素数问题,什么是反素数可以自行百度,问题就转化为了求最接近 n n n 的反素数,反素数可以暴力求出和或者打表,下面的代码给出了暴力的过程
这题同时由于给出了顺序,所以这个退出的过程就必须模拟
那么问题就在于怎么处理这个退出的过程了,要实现 l o g ( n ) log(n) log(n) 的时间,这里引入线段树的单点更新,本题和 P O J POJ POJ 2828 2828 2828很相似,可以点进去看一下
代码思路:
和 P O J POJ POJ 2828 2828 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;elsek=((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]);}
}