POJ 2886 Who Gets the Most Candies?

题目链接:点击打开链接


题意:输入N,K,表示N个小朋友围成一个圈,每个小朋友手拿一张卡片上写着一个数,有正有负,开始时顺时针第K个小朋友出队,然后如果他手里的卡片数m是正数下一个出队的就是他左侧第m个,如果是负的就是右侧第m个,第i个出队的小朋友可以获得 i 的约数个糖果,问所有小朋友都出队后获得最多糖果的是谁,输出名字和糖果数。


本题需要一个知识:反素数,对于一个数n,如果所有小于n的数的约数都没有n多,则n是反素数。具体请百度百科。本题只要提前打表出反素数,然后找出小于N且最接近N的反素数,就可以知道获得最多糖果的是第几个出队的小朋友。


求反素数的代码:(dfs搜索,剪枝需要两条性质,请百度百科反素数)

#include <cstdio>
#include <iostream>

using namespace std;
int prime[20]={1,2,3,5,7,11,13,17,19,23,31,34};
int bestsum,maxnum;
int n;
void get(int cur,int k,int num,int limit){
    int i,temp;
    if(k>10)
        return ;
    if(num>maxnum){
            maxnum=num;
            bestsum=cur;
    }
    if(num==maxnum&&cur<bestsum){
            bestsum=cur;
    }
    temp=cur;
    for(int i=1;i<=limit;i++){
          if(temp*prime[k]>=n)
            break;
          temp*=prime[k];
          get(temp,k+1,num*(i+1),i);
    }
}
int antiprime[50];
int divnum[50];
int main(){
    n=500010;
    for(int i=35;i>=1;i--){

            maxnum=0;bestsum=0;
            get(1,1,1,20);
            antiprime[i]=bestsum;
            divnum[i]=maxnum;
            n=bestsum;
    }
    for(int i=1;i<=35;i++){
            cout<<antiprime[i]<<" "<<divnum[i]<<endl;
    }
    return 0;
}

然后每次需要查询当前队中第i个小朋友的位置,这就需要线段树来维护每个区间的小朋友数。

注意设每次要查询的小朋友数为next,当前卡片上的数是正和负时求下一个next方法不一样,如果是正的因为之前位置的人出队了,所以实际查询的人数应该-1,是负的则不受影响。

本题完整代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define MAX 500010
using namespace std;
int antiprime[40]={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,554400};
int divnum[40]={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,216};
int sum[MAX<<2];
int N,K;
char name[MAX][20];
int card[MAX];
void pushup(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void build (int l,int r,int rt){
    if(l==r){sum[rt]=1;return ;}
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);
}

int query(int k,int l,int r,int rt){
    if(l==r){
        sum[rt]--;
        return l;
    }
    int m=(l+r)>>1;
    int res;
    if(sum[rt<<1]>=k) res=query(k,lson);
    else {
        k-=sum[rt<<1];
        res=query(k,rson);
    }
    pushup(rt);
    return res;
}

void init(){
    for(int i=1;i<=N;i++){
            scanf("%s%d",name[i],&card[i]);
    }
    build(1,N,1);
}

int main(){
    while(~scanf("%d%d",&N,&K)){
            init();
            int nowP=K;
            int next=K;
            int pripos=1;
            while(antiprime[pripos]<=N) pripos++;
            pripos--;

            for(int i=1;i<=antiprime[pripos];i++){
                nowP=query(next,1,N,1);

                if(sum[1]==0)break;
                if(card[nowP]>0){
                    next=(card[nowP]%sum[1]+next-1)%sum[1];
                }
                else{
                    next=(card[nowP]%sum[1]+next)%sum[1];
                }
                if(next<=0)next+=sum[1];
            }
            printf("%s %d\n",name[nowP],divnum[pripos]);
    }
    return 0;
}








你可能感兴趣的:(线段树,反素数)