POJ 2886 Who Gets the Most Candies?(線段樹)

題意:N 個小孩圍成一圈,他們被順時針編號爲 1 到 N。每個小孩手中有一個卡片,上面有一個非 0 的數字,遊戲從第 K 個小孩開始,他告訴其他小孩他卡片上的數字並離開這個圈,他卡片上的數字 A 表明了下一個離開的小孩,如果 A 是大於 0 的,則下個離開的是左手邊第 A 個,如果是小於 0 的,則是右手邊的第 -A 個小孩。遊戲將直到所有小孩都離開,在遊戲中,第 p 個離開的小孩將得到 F(p) 個糖果,F(p) 是 p 的約數的個數,問誰將得到最多的糖果。輸出最幸運的小孩的名字和他可以得到的糖果。

思路:其實只要知道P=max{F(i)}(1<=i<=N)就可以了,P就是反素數,然後用線段樹加速模擬小孩出圈的過程。

對於反素數的求法,先用篩法求出50w以內的素數,並記錄下每個數的任意一個因數。現在設f(i)爲i的約數個數,如果i是素數,則f(i)=2。如果i是合數,令i=a1^b1*a2^b2*…*an^bn,則i的約數個數爲(b1+1)*(b2+1)*...*(bn+1)。因此,可以用遞推式f(i)=f(i/ak)*(bk+1)/bk(其中ak是i的任意一個約數)。其實還可以打表記錄反素數,這裏不作介紹。

線段樹的維護,每次用二差查找的方法尋找每次出圈孩子的位置並更新就可以了。具體方法和POJ 2828類似,這裏不作詳細說明。

注意卡片上的值爲正和爲負的時候,計算位移時應該分開討論。

#include<cstdio>
#include<cstring>
#define M ((L+R)>>1)
#define ls (o<<1)
#define rs (o<<1|1)
#define lson ls,L,M
#define rson rs,M+1,R
#define MAXN 500005
char name[MAXN][12];
int n,pos,A[MAXN],sumv[MAXN*3],num;
//sumv[o]儲存根o的區間內還剩多少個小孩,初始化時葉子結點爲1
//線段樹的維護
void pushup(int o) {sumv[o]=sumv[ls]+sumv[rs];}
void build(int o,int L,int R)
{
    if(L==R) {sumv[o]=1;return;}
    build(lson); build(rson);
    pushup(o);
}
void update(int o,int L,int R,int sum)
{
    if(L==R) {sumv[o]=0;num=L;return;}
    sumv[ls]+sum>=pos? update(lson,sum):update(rson,sum+sumv[ls]);
    pushup(o);
}
//==========================================/
//求約數個數部分
int f[MAXN],fmax[MAXN],fmaxnum[MAXN],prime[MAXN],pnum=0,yinshu[MAXN];
bool isprime[MAXN];
void init_fmax()
{
    memset(isprime,0,sizeof(isprime));
    f[1]=fmax[1]=fmaxnum[1]=isprime[0]=isprime[1]=1;
    for(int i=2;i<MAXN;++i)
    {
        if(!isprime[i]) prime[pnum++]=i;
        for(int j=0;j<pnum && prime[j]*i<MAXN;++j)
        {
            isprime[prime[j]*i]=1;
            yinshu[prime[j]*i]=prime[j]; //記錄約數
            if(i%prime[j]==0) break;
        }
        if(!isprime[i]) f[i]=2;
        else
        {
            int tn=i,bi=0;
            while(tn>1 && tn%yinshu[i]==0) tn/=yinshu[i],++bi;
            f[i]=f[i/yinshu[i]]*(bi+1)/bi;
        }
        if(f[i]>fmax[i-1]) fmax[i]=f[i],fmaxnum[i]=i;
        else fmax[i]=fmax[i-1],fmaxnum[i]=fmaxnum[i-1];
    }
}
//========================================================/
int main()
{
    init_fmax();
    while(~scanf("%d%d",&n,&pos))
    {
        for(int i=1;i<=n;++i) scanf(" %s %d",name[i],&A[i]);
        build(1,1,n);
        for(int i=1;i<fmaxnum[n];++i)//剩下n-i個人
        {
            update(1,1,n,0);
            if(A[num]<0) pos+=A[num]-1;
            else pos+=A[num]-2;
            while(pos<0) pos+=(n-i);
            pos=pos%(n-i)+1;
        }
        update(1,1,n,0);
        printf("%s %d\n",name[num],fmax[n]);
    }
    return 0;
}


發佈了39 篇原創文章 · 獲贊 30 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章