Who Gets the Most Candies?(约瑟夫环+反素数+插队)

传送门POJ - 2886

描述

N children are sitting in a circle to play a game.
The children are numbered from 1 to N in clockwise order. Each of them has a card with a non-zero integer on it in his/her hand. The game starts from the K-th child, who tells all the others the integer on his card and jumps out of the circle. The integer on his card tells the next child to jump out. Let A denote the integer. If A is positive, the next child will be the A-th child to the left. If A is negative, the next child will be the (−A)-th child to the right.
The game lasts until all children have jumped out of the circle. During the game, the p-th child jumping out will get F(p) candies where F(p) is the number of positive integers that perfectly divide p. Who gets the most candies?

输入

There are several test cases in the input. Each test case starts with two integers N (0 < N ≤ 500,000) and K (1 ≤ K ≤ N) on the first line. The next N lines contains the names of the children (consisting of at most 10 letters) and the integers (non-zero with magnitudes within 108) on their cards in increasing order of the children’s numbers, a name and an integer separated by a single space in a line with no leading or trailing spaces.

输出

Output one line for each test case containing the name of the luckiest child and the number of candies he/she gets. If ties occur, always choose the child who jumps out of the circle first.

样例

  • Input
    4 2
    Tom 2
    Jack 4
    Mary -1
    Sam 1
  • Output
    Sam 3

思路

  • 题意:约瑟夫出列问题,给出人数n和初始出列位置k,然后给出每个人的id和val,val>0则从k右边数val个,val<0则从k左边数val个。样例出队顺序:jack->Mary->Tom->Sam,我们给他们顺序标号tem,那么每个人出队的时候都可以得到p(tem)个糖果,p(tem)表示tem的因子个数,如p(4)=3(1,2,4);求得到糖果最多的人和糖果数0则从k左边数val个。样例出队顺序:jack->
  • 我们通过k和当前的val[i]可以确定下一个要出队的人是 剩下的 队伍中的第几个:若val>0,从k-1(因为自己被移除了)往后面数val个然后%剩余人数,即:k=(k-1+val[i])%(n-i)为了防止1%1=0的情况,写成这种方式:k=(k-1+val[i]-1)%(n-i)+1;若val<0,则从k往前数val个再加上剩余人数,然后%剩余人数,同样要防止1%1=0的情况,即`k=((k+mes[tem].val-1)%(n-i)+n-i)%(n-i)+1;`。 样例的k值为2-="">2->1->1。0,则从k往前数val个再加上剩余人数,然后%剩余人数,同样要防止1%1=0的情况,即`k=((k+mes[tem].val-1)%(n-i)+n-i)%(n-i)+1;`。>
  • 确定了下一个出队的人在第几个剩余人中的第几个之后,就要通过线段树插队找到他原来的位置,如果他现在是第k个,而前面k已经有人了,那么就往后摞,直到有位置可以放,这个位置就是它原来的位置。
  • 反素数:对于数n,任何小于n的数的因子数都小于n的因子数,那么n就是一个反素数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int ip[] = {0,1, 2, 4, 6, 12, 24, 36, 48, 60, 120, 180, 240, 360, 720,
    840, 1260, 1680, 2520, 5040, 7560, 10080, 15120, 20160,
    25200, 27720, 45360, 50400, 55440, 83160, 110880,
    166320, 221760, 277200, 332640, 498960, 500001
    }; //反素数

    int div1[] = {0,1, 2, 3, 4, 6, 8, 9, 10, 12, 16, 18, 20, 24, 30, 32, 36,
    40, 48, 60, 64, 72, 80, 84, 90, 96, 100, 108, 120,
    128, 144, 160, 168, 180, 192, 200
    };//反素数对应的约数个数

我们要找到小于n且最接近n的反素数,答案就是它的因子个数。
也可以直接打表求每个数的因子个数

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
using namespace std;
#define CRL(a) mamset(a,0,sizeof(a))
typedef long long ll;
const int N=5e5+5;
int tree[N*4];
int ans[N];
int ip[] = {0,1, 2, 4, 6, 12, 24, 36, 48, 60, 120, 180, 240, 360, 720,
840, 1260, 1680, 2520, 5040, 7560, 10080, 15120, 20160,
25200, 27720, 45360, 50400, 55440, 83160, 110880,
166320, 221760, 277200, 332640, 498960, 500001
}; //反素数

int div1[] = {0,1, 2, 3, 4, 6, 8, 9, 10, 12, 16, 18, 20, 24, 30, 32, 36,
40, 48, 60, 64, 72, 80, 84, 90, 96, 100, 108, 120,
128, 144, 160, 168, 180, 192, 200
};//反素数对应的约数个数
struct node{
char s[20];//string直接t掉
int val;
}mes[N];
void build(int l,int r,int d){
tree[d]=r-l+1;
if(l==r)return;
int mid=(l+r)/2;
build(l,mid,d<<1);
build(mid+1,r,d<<1|1);
}
int update(int l,int r,int num,int d){
tree[d]--;
if(l==r)return l;
int mid=(l+r)/2;
if(num<=tree[d<<1]) return update(l,mid,num,d<<1);
else return update(mid+1,r,num-tree[d<<1],d<<1|1);
}
int main()
{
ios::sync_with_stdio(false);
int n,k,tem,res;
while(cin>>n>>k){
res=0;
for(int i=1;ip[i]<=n;i++) res=i;
build(1,n,1);
for(int i=1;i<=n;i++)
cin>>mes[i].s>>mes[i].val;
for(int i=1;i<=n;i++){
tem=update(1,n,k,1);
if(i==ip[res]){
cout<<mes[tem].s<<" "<<div1[res]<<endl;
break;
}
if(mes[tem].val>0) k=(k-1+mes[tem].val-1)%(n-i)+1;
else k=((k+mes[tem].val-1)%(n-i)+n-i)%(n-i)+1;
}
}
return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值