[POJ2886]Who Gets the Most Candies?

Posted hjj1871984569

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[POJ2886]Who Gets the Most Candies?相关的知识,希望对你有一定的参考价值。

题目:Who Gets the Most Candies?

链接:http://poj.org/problem?id=2886

分析:

1)这是一个约瑟夫环,关键在于如何定位下一个位置。如同楼层没有0层一样,针对card需要分正负讨论,计算出在新环中下一个离开的位置k。

2)求出k在原环中的位置。采用数据结构维护一下。

3)更新答案,移除小朋友,计算新的位置,直到结束。

4)求反素数,用类似筛法的方式计算一下。当然也可以打表。

5)有一个小优化,求出最优解所需的步数,就不用等到到全部小朋友都离开啦。

方法一:二分+树状数组

二分位置mid,树状数组维护 位置mid前 还有多少小朋友在现在的约瑟夫环中存在。

更新答案并在树状数组中移除这个小朋友。

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
const int maxN=500005;
char a[maxN+5][10];
int n,b[maxN+5],f[maxN+5],T[maxN+5];
void Tadd(int x,int y){for(int i=x;i<=n;i+=i&-i)T[i]+=y;}
int Tque(int x){int ret=0;for(int i=x;i;i-=i&-i)ret+=T[i];return ret;}
void Init(){
    for(int i=1,k=(int)(sqrt(double(maxN)+0.5));i<=k;++i){
        for(int j=i+1;j*i<=maxN;++j)f[j*i]+=2;
        ++f[i*i];
    }
}
int main(){
    Init();
    for(int k,ans0,ans1;~scanf("%d%d",&n,&k);){
        for(int i=1;i<=n;++i)T[i]=0;
        for(int i=1;i<=n;++i){scanf("%s %d",a[i],&b[i]);}
        for(int i=1;i<=n;++i)Tadd(i,1);
        ans1=0;
        for(int num=n,t=0;1;){
            int l=1,r=n,mid;
            for(;l<=r;){
                mid=(l+r)>>1;
                if(Tque(mid)<k)l=mid+1;else r=mid-1;
            }
            Tadd(l,-1);
            ++t;
            if(f[t]>ans1){ans0=l;ans1=f[t];}
            --num;
            if(!num)break;
            if(b[l]>0){
                k=((k-2+num)%num+b[l])%num+1;
            }else{
                k=((k+b[l]-1)%num+num)%num+1;
            }
        }
        printf("%s %d
",a[ans0],ans1);
    }
    return 0;
} 

 

方法二:线段树

每个节点维护这个区间有多少小朋友,在线段树中寻找第k个小朋友的位置pos。

更新答案并在线段树中移除这个小朋友。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxN=500005;
char a[maxN+5][10];
int n,pos,b[maxN+5],f[maxN+5];
struct SegmentTree{
    int val[maxN<<2];
    void init(int v,int l,int r){
        if(l==r){val[v]=1;return;}
        int mid=(l+r)>>1;
        init(v<<1,l,mid);init((v<<1)+1,mid+1,r); 
        val[v]=val[v<<1]+val[(v<<1)+1];
    }
    void Modify(int v,int l,int r,const int &k){
        if(l==r){--val[v];pos=l;return;}
        int mid=(l+r)>>1;
        if(k<=val[v<<1])Modify(v<<1,l,mid,k);else Modify((v<<1)+1,mid+1,r,k-val[v<<1]);
        --val[v];
    }
}T;
void Init(){
    for(int i=1,k=(int)(sqrt(double(maxN)+0.5));i<=k;++i){
        for(int j=i+1;j*i<=maxN;++j)f[j*i]+=2;
        ++f[i*i];
    }
}
int main(){
    Init();
    for(int k,ans0,ans1;~scanf("%d%d",&n,&k);){
        memset(T.val,0,sizeof T.val);
        for(int i=1;i<=n;++i){scanf("%s %d",a[i],&b[i]);}
        T.init(1,1,n);
        ans1=0;
        for(int t=0,num;1;){
            T.Modify(1,1,n,k);
            ++t;
            if(f[t]>ans1){ans0=pos;ans1=f[t];}
            num=T.val[1];
            if(!num)break;
            if(b[pos]>0){
                k=((k-2+num)%num+b[pos])%num+1;
            }else{
                k=((k+b[pos]-1)%num+num)%num+1;
            }
        }
        printf("%s %d
",a[ans0],ans1);
    }
    return 0;
} 

 

PS:反素数表:

int max_turn[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 max_candy[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};

 

以上是关于[POJ2886]Who Gets the Most Candies?的主要内容,如果未能解决你的问题,请参考以下文章

[poj 2886] Who Gets the Most Candies? 线段树

[POJ2886]Who Gets the Most Candies?

(线段树,反素数)poj2886-Who Gets the Most Candies?

poj2886 Who Gets the Most Candies?

POJ 2886 Who Gets the Most Candies? (线段树)

POJ 2886 Who Gets the Most Candies?