poj 2886 Who Gets the Most Candies?

来源:互联网 发布:淘宝推广手段 编辑:程序博客网 时间:2024/04/27 13:23

题意N 个小孩围成一圈,他们被顺时针编号为 1 到 N。每个小孩手中有一个卡片,上面有一个非 0 的数字,游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,他卡片上的数字 A 表明了下一个离开的小孩,如果 A 是大于 0 的,则下个离开的是左手边第 A 个,如果是小于 0 的,则是右手边的第 -A 个小孩。游戏将直到所有小孩都离开,在游戏中,第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。

思路:看到这题感觉完全摸不清头绪,只好去参考大神的代码,看完才知道还有反素数这玩意,对应一个y,任意x<y都满足F(x)<F(y),这样的y就是反素数,明显的DP问题,可以先DP一下,然后直接打表。现在只要找到最大的小于等于n的反素数,这个数就是第几个出去的小孩是幸运小孩。那个卡片的问题,对于 k 位置的 孩子,他的 数字是正数 那么因为他自己本身是要被踢走的,所以相对位置 为k= k+num-1如果数字是负数,那么按正着数就没影响,k=k-num。线段树存储当前区间共有多少个人,每一次找到第k (前面有k-1个)个孩子,经过的区间都要 -1,然后记录被踢走的孩子编号。

线段树的值存储这个区间还剩下的小孩数目,其余的全部和2828是一模一样了。

#include<cstdio>#include<cstring>#include<iostream>#define L(u) (u<<1)#define R(u) (u<<1|1)using namespace std;const int N = 500001;char name[N][11];int card[N];int rfact[37]={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,500001};int candy[37]={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,1314521};struct Node{int r,l,sum;};Node node[N<<2];void build(int u,int left,int right){    node[u].l=left; node[u].r=right;    if(left==right)    {        node[u].sum = 1;        return;    }    int mid = (left+right)>>1;    build(L(u),left,mid);    build(R(u),mid+1,right);    node[u].sum = right-left+1;}int query(int u,int num){    --node[u].sum;    if(node[u].l==node[u].r)        return node[u].l;    if(num<=node[L(u)].sum)        return query(L(u),num);    return query(R(u),num-node[L(u)].sum);}int main(void){    int n,k,p,sum;    while(~scanf("%d %d",&n,&k))    {       int i = 0;      while(rfact[i]<=n)//求反素数的最大的        i++;        i -= 1;      p = rfact[i];      sum = candy[i];      build(1,1,n);      for(i=1;i<=n;++i)      scanf("%s %d",name+i,card+i);      k--;      int num;      for(i=1;i<=p;++i)      {          n--;//人数每次减一          num = query(1,k+1);          if(i==p)          break;          if(card[num]>0)             k = (k+card[num]-1)%n;          else             k = ((k+card[num])%n+n)%n;      }      printf("%s %d\n",name[num],sum);    }    return 0;}


原创粉丝点击