POJ2886 Who Gets the Most Candies?(反素數+線段樹模擬約瑟夫環)

傳送門:http://poj.org/problem?id=2886
這題真是太厲害了。。
我們要先知道反素數,推薦: http://blog.csdn.net/ACdreamers/article/details/25049767
這題很多人都是打表反素數的,我暴力預處理了一下,雖然有點慢,最後也是4000+ms過的。。
這題的答案一定爲反素數,因爲小於這個反素數的數的因子數一定比反素數少,所以求出<=n的最大的反素數,就一定是1-n中因子數最多的數字。知道這個結論以後,用線段樹模擬一下約瑟夫環的跳轉過程。

if(val[pos]>0){
    k=(((k-1+val[pos]-1)%mod)+mod)%mod+1;
    //k-1表示去掉本身,val[pos]-1在後面+1回來是爲了防止k=0
}
else{
     k=(((k+val[pos]-1)%mod)+mod)%mod+1;
     //往前找的時候k不用去掉,所以k不用-1
 }

然後每次把k更新到線段樹裏,類似於POJ 2828 排隊那題

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define mp make_pair
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define calm (l+r)>>1
const int INF = 1e9+7;

const int maxn=500010;

bool vis[110];
vector<int> prime;
int antiprime[300],num[300];
void init(){//打表反素數
    for(int i=2; i<=50; i++){
        if(!vis[i]){
            prime.pb(i);
            for(int j=i+i; j<=50; j+=i){
                vis[j]=true;
            }
        }
    }
    memset(antiprime,127,sizeof antiprime);
    antiprime[1]=1;
    for(int i=1;i<=maxn;i++){
        int ans=1;
        for(int j=0,len=prime.size(); j<len; j++){
            if(i%prime[j]==0){
                int tot=1,t=i;
                while(t%prime[j]==0){
                    t/=prime[j];tot++;
                }
                ans*=tot;
            }
            antiprime[ans]=min(antiprime[ans],i);
        }
    }
    sort(antiprime,antiprime+300);
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299])break;
        int ans=1;
        for(int j=0,len=prime.size(); j<len; j++){
            if(antiprime[i]%prime[j]==0){
                int tot=1,t=antiprime[i];
                while(t%prime[j]==0){
                    t/=prime[j];tot++;
                }
                ans*=tot;
            }
        }
        num[i]=ans;
    }
    int now=0;
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299]){break;}
        if(num[i]<=now)antiprime[i]=antiprime[299];
        now=max(now,num[i]);
    }
    sort(antiprime,antiprime+300);
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299]){break;}
    }
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299])break;
        int ans=1;
        for(int j=0,len=prime.size(); j<len; j++){
            if(antiprime[i]%prime[j]==0){
                int tot=1,t=antiprime[i];
                while(t%prime[j]==0){
                    t/=prime[j];tot++;
                }
                ans*=tot;
            }
        }
        num[i]=ans;
    }
}

int n,k;
char name[maxn][13];
int val[maxn];
int sum[maxn<<2];
void build(int l,int r,int rt){
    sum[rt]=r-l+1;
    if(l==r){
        return;
    }
    int m=calm;
    build(lson);build(rson);
}
int update(int x,int l,int r,int rt){
    sum[rt]--;
    if(l==r)return l;
    int m=calm;
    if(x<=sum[rt<<1])return update(x,lson);
    return update(x-sum[rt<<1],rson);
}
int main()
{
    //freopen("D://input.txt","r",stdin);
    init();
    while(scanf("%d%d",&n,&k)!=EOF){
        for(int i=1;i<=n;i++){
            scanf("%s%d",name[i],&val[i]);
        }
        int cnt=0;
        for(int i=0;i<35;i++){
        //答案一定爲反素數,因爲小於這個反素數的數的因子數一定比反素數少
            if(antiprime[i]<=n)cnt=i;
        }
        //printf("%d\n",antiprime[cnt]);
        build(1,n,1);
        int &mod=sum[1];//圈內總人數
        int pos=0;
        val[0]=0;//先等於0,使第一個人爲k
        for(int i=0;i<antiprime[cnt];i++){
            if(val[pos]>0){
                k=(((k-1+val[pos]-1)%mod)+mod)%mod+1;
                //k-1表示去掉本身,val[pos]-1在後面+1回來是爲了防止k=0
            }
            else{
                k=(((k+val[pos]-1)%mod)+mod)%mod+1;
                //往前找的時候k不用去掉,所以k不用-1
            }
            pos=update(k,1,n,1);
        }
        printf("%s %d\n",name[pos],num[cnt]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章