您的位置:首页 > 其它

POJ:2886 Who Gets the Most Candies?

2014-03-13 19:08 387 查看
线段树模拟约瑟夫环+反素数。

网上说这个地方是模拟约瑟夫环,其实我感觉没有约瑟夫环那样麻烦。实际上只需要求出去掉某个人之后,卡牌上对应的人从左往右数是第几个即可。这样假设第k个人出列,它对应的编号是A。如果A大于0说明对应的人在k的右边,否则左边。原来人数是n,k出列以后就是n-1了。那么这个A对应的是从左往右第(k-1+A-1)%(n-1)+1。前面的

k-1表示编号从0开始这是为了方便取余,括号里后面有个-1意思是去掉k这个人,所以要减一,因为A在右边,最后的+1是为了恢复编号从1开始。如果A小于0则((k-1+A)%(n-1)+(n-1))%(n-1)+1因为此时A在左边,其实相当于减了一个数因为A是负的,k在它右边所以不用考虑-1,注意为了避免结果为负,要加上一个模。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#define ll  long long
#define INF 2139062143
#define inf -2139062144
#define MOD 20071027
#define MAXN  500005
#define LEN 555555<<2
using namespace std;
char name[MAXN][15];
int val[MAXN];
struct Segment_tree
{
private:

    int sum[LEN],sz;
    void pushup(int o)
    {
        sum[o]=sum[o<<1]+sum[o<<1|1];
    }
    int myquery(int o,int L,int R,int v)
    {
        sum[o]--;
        if(L==R)  return L;
        int M=(L+R)/2;
        if(v<=sum[o*2]) return myquery(o*2,L,M,v);
        else return myquery(o*2+1,M+1,R,v-sum[o*2]);
    }
public:
    void clear(int n)
    {
        sz=n;
        int len=n<<2;
        for(int i=0; i<=len; ++i)
            sum[i]=0;
    }
    void build(int o,int L,int R)
    {
        if(L==R)
        {
            scanf("%s%d",&name[L],&val[L]);
            sum[o]=1;
        }
        else
        {
            int M=(L+R)>>1;
            build(o<<1,L,M);
            build(o<<1|1,M+1,R);
            pushup(o);
        }
    }
    int query(int p)
    {
        return  myquery(1,1,sz,p);
    }
};
Segment_tree tree;
int RPrime[]=
{
    //反素数
    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 fact[]=
{
    //反素数约数个数
    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 main()
{
    int n,k;
    //freopen("outmy.txt","w",stdout);
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        tree.clear(n);
        int v=0;
        for(int i=0; RPrime[i]<=n ; ++i)v=i;
        tree.build(1,1,n);
        int p=k,u=0;
        for(int i=1,j=n-1; i<=RPrime[v]; ++i,--j)
        {
            u=tree.query(p);
            if(j==0) break;
            if(val[u]>0)
                p=((p-2+val[u])%j+j)%j+1;
            else
                p=((p-1+val[u])%j+j)%j+1;
        }
        printf("%s %d\n",name[u],fact[v]);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: