Who Gets the Most Candies? POJ - 2886

state 传送门:http://poj.org/problem?id=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?
Input
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

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.
Sample Input

4 2
Tom 2
Jack 4
Mary -1
Sam 1

Sample Output

Sam 3

线段树处理约瑟夫环问题。
叶节点存储该位置上是否还有人,有人为1,没人为0。父节点存储在该区间还有多少个人。初始化时,线段树每个节点记录其对应区间的长度。

因子个数采用打表的方法,做法与素数筛选相似。存在整数X,其N倍为Y,则X为Y的一个因数。
if(X*N==Y) numOfFactor[Y]++;
反素数留个坑
Ps:1是任何数的因数;一个数也是其自身的因数

采用模拟的方式,首先找到将要jump out 的位置,记录这个位置和下一次执行的步数,将其从环中清除,更新环的长度。重复直至所有人都离开环。

有了这个大体的流程以后,再详细的填充具体的情况。
在循环中,需要清楚从当前位置向左/右 X (X为非零整数)个位置,将题目所给输入的一个视为环的起点,如果能知道当前位置(A)到起点还有多少多少人,也就是A的排名,我们可以通过数学知识来得到下一个我们要清除的位置,具体公式不做说明。介于题目中提及,每张卡片上的数字不超过1e8,这出一个提示,移动的距离可能超出环的长度,但是环的形状决定了这样仍然是正确的。为了便于计算,需要将移动的距离对环的长度取余(这是为什么要更新环长度的原因)。在取余的时候要注意环的长度为1时取余的情况,既然环里就剩一个人了,不如操作一波直接退出。还有余数为0的情况。然后回头看一下给的数据,考虑有没有特判。如果题目给出输入中,N==1时,可以直接输出。
Ps:
1、注意题目中的左右。左为顺时针方向,右为逆时针方向
2、建议向下翻到代码结束


#include <cstdio>
#include <algorithm>
#define INF 0x3f3f3f3f
#define nLimit 500000
using namespace std;
struct tree{
    int left,right;
    int val;
};
struct record{
    char name[20];
    int val;
};

int N,K,len;
int nowPos,nextPos;
int candy[nLimit+100];
record C[nLimit+100];
tree T[10002400];
void make(int l,int r,int pos);
void update(int num,int pos);
int getSum(int l,int r,int pos);
void inquire(int num,int pos);
int main(){
    for(register int i=1;i<=500000;i++)
        candy[i]=0;
    for(register int i=1;i<=500000;i++){
        candy[i]++;
        for(register int j=i+i;j<=500000;j+=i)
            candy[j]++;
    }
    while(scanf("%d %d",&N,&K)!=EOF){
        int num=1,finish;
        for(register int i=1;i<=N;i++){
            scanf("%s %d",C[i].name,&C[i].val);
            if(num<candy[i]){
                num=candy[i];
                finish=i;
            }
        }
        if(N==1){
            printf("%s %d\n",C[1].name,num);
            continue;
        }
        make(1,N,1);
        len=N-1;
        int pre,sul;
        update(K,1);
        nowPos=K,nextPos=C[K].val;
        //printf("nowPos=%d\n",nowPos);
        for(register int i=1;i<finish;i++){
            if(len==1){
                inquire(1,1);
                break;
            }
            pre=getSum(1,nowPos,1),sul=len-pre;
            //printf("pre=%d sul=%d\n",pre,sul);
            if(nextPos<0){      //go left
                nextPos=abs(nextPos)%len;
                if(nextPos<=pre)
                    inquire(pre-nextPos+1,1);
                else
                    inquire(len-nextPos+pre+1,1);
            }else{              //go right
                nextPos=abs(nextPos)%len;
                if(nextPos<=sul)
                    inquire(nextPos+pre,1);
                else
                    inquire(nextPos-sul,1);
            }
            len--;
            //printf("nowPos in loop=%d\n",nowPos);
        }
        //printf("nowPos=%d\n",nowPos);
        printf("%s %d\n",C[nowPos].name,num);
    }
    return 0;
}
void make(int l,int r,int pos){
    T[pos].left=l,T[pos].right=r;
    T[pos].val=r-l+1;
    if(l==r){
        return;
    }
    int mid=l+(r-l)/2;
    make(l,mid,pos<<1);
    make(mid+1,r,pos<<1|1);
}
void update(int num,int pos){
    int l=T[pos].left;
    int r=T[pos].right;
    T[pos].val--;
    if(l==r)
        return;
    int mid=l+(r-l)/2;
    if(num<=mid)
        update(num,pos<<1);
    else
        update(num,pos<<1|1);
}
int getSum(int l,int r,int pos){
    if(l<=T[pos].left&&T[pos].right<=r){
        return T[pos].val;
    }
    int mid=T[pos].left+(T[pos].right-T[pos].left)/2;
    int sum=0;
    if(l<=mid)
        sum+=getSum(l,r,pos<<1);
    if(mid<r)
        sum+=getSum(l,r,pos<<1|1);
    return sum;
}
void inquire(int num,int pos){
    T[pos].val--;
    if(T[pos].left==T[pos].right){
        nextPos=C[T[pos].left].val;
        nowPos=T[pos].left;
        return;
    }
    if(num<=T[pos<<1].val)
        inquire(num,pos<<1);
    else
        inquire(num-T[pos<<1].val,pos<<1|1);
}

其实这一段代码有一行是错误的,错在由当前位置到下一个位置的公式上,在一个情况下会导致求得的排名数大于当时环的长度,由void inquire(int num,int pos) 返回最后一名,导致结果错误。但是我是不会说的,建议自己想一下嘛。不行就试一下最后一组测试数据。


关于可能遇到的错误说明:

1、在int getSum(int l,int r,int pos) 函数中,如果传入参数 l>r,将导致返回条件不能实现,造成自身的无限调用,导致数组越界。
2、查询操作中,如果未对线段树初始化(就是没写 make 函数),使函数不能达成返回条件,导致数组越界。
3、在void inquire(int num,int pos)函数中,如果参数 num 大于有效叶节点的个数,会访问到最后一个有效叶节点,并可能造成后续操作的错误。


测试数据
6 2
A -1
B -1
C 2
D 3
E 5
F 1

6 3
A 100
B -6
C 11
D 12
E 2
F 3

6 3
A 1
B 2
C -3
D -3
E -4
F -5

6 3
A -2
B 3
C -5
D 4
E 10
F 2

8 1
A 1
B 1
C 1
D 1
E 1
F 1
G 1
H 1

6 6
A 3
B -8
C -4
D -7
E 9
F -8

6 2
A 2
B 4
C 9
D -3
E -3
F -2

6 6
A 3
B 2
C 1
D 1
E 1
F -5

还不行用没有删除的注释跑一下看看

猜你喜欢

转载自blog.csdn.net/white_156/article/details/82025636
今日推荐