題目:http://poj.org/problem?id=2886
題意:
有一個n個人圍成圓圈,每次有一個人出圈,下一次出圈的人由本次出圈的人決定。
按照出圈的順序,每個人可以獲得不同的糖果,找出可以獲得最多糖果的人。
第p個出圈的人可以獲得F(p)個糖果。
思路:
用線段樹維護剩餘的人數。
約瑟夫環+線段樹+反素數
#include <iostream>
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 500005;
int tree[maxn << 2];
struct child{
char name[20];
int card;
}cc[maxn];
void PushUp(int rt){
tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
void build(int l, int r, int rt){
tree[rt] = r-l+1;
if (l == r)
return;
int m = (l + r) >> 1;
build (lson);
build (rson);
PushUp(rt);
}
void update(int p, int l, int r, int rt){
if (l == r) {
tree[rt] = 0; return;
}
int m = (l + r) >> 1;
if (m >= p) update(p,lson);
else update(p, rson);
PushUp(rt);
}
int query(int k, int l, int r, int rt){
if (l == r)
return l;
int m = (l + r) >> 1;
if (tree[rt << 1] >= k)
return query(k, lson);
else
return query(k - tree[rt << 1], rson);
}
int hash[maxn];
void init(){//打出反素數表
for (int i = 1; i <= maxn; i ++){
hash[i] ++;
for (int j = 2*i; j <= maxn; j += i)
hash[j] ++;
}
}
int main(){
init();
int n, k;
while (scanf ("%d %d", &n, &k) != EOF){
for(int i = 1;i <= n;i++)
scanf("%s%d",cc[i].name,&cc[i].card);
build (1, n, 1);
int maxtemp = hash[1];
int kk = 1;
for(int i = 2;i <= n;i++){
if(maxtemp<hash[i]){
maxtemp = hash[i];
kk = i;
}
}
int pos = k; //k爲人在約瑟夫環中的相對位置,pos爲該人在最初的環中的絕對位置
for(int i = 1;i < kk;i++) {
update (pos, 1, n, 1); //刪除當前這個人
//求出下一個人的在約瑟夫環中的相對位置
k += cc[pos].card;
if (cc[pos].card > 0) k--; //注意:當num[pos]>0時,pos後面的人的相對位置都要減1
k = (k % tree[1] + tree[1]) % tree[1]; //sum[1]爲剩餘人的人數
if (k == 0) k = tree[1];
//由相對位置求出絕對位置
pos = query(k, 1, n, 1);
}
printf ("%s %d\n", cc[pos].name,hash[kk]);
}
return 0;
}