poj 2886(线段树+反素数)

#include <cstdio>
#include <iostream>
#include <algorithm>
#define lson L , m , rt << 1
#define rson m + 1 , R , rt << 1 | 1
using namespace std;
const int maxn =  520000;
struct Node
{
    int L,R;
    int sum;
}tree[maxn*4];
int n,k;
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
};
void builttree(int L,int R,int rt)
{
    tree[rt].L=L;
    tree[rt].R=R;
    tree[rt].sum=R-L+1;
    if(L==R)
    return ;
    int m=(L+R)/2;
    builttree(lson);
    builttree(rson);
}
int ans[maxn];
int Query(int rt,int pos)
{
    tree[rt].sum--;
    if(tree[rt].L==tree[rt].R)
    {
      return tree[rt].L;
    }
    int m=(tree[rt].L+tree[rt].R)/2;
    if(tree[rt*2].sum>=pos)
      Query(rt*2,pos);//左孩子可以容下则进左孩子
    else
      Query(rt*2+1,pos-tree[rt*2].sum);//左孩子已满
}

char strname[maxn][20];
int val[maxn];

int main()
{
  while(scanf("%d%d",&n,&k)!=EOF)
  {
   builttree(1,n,1);
   int p;
   int maxx=0;
   for(int i=1;i<=n;i++)
   {
      scanf("%s %d", &strname[i], &val[i]);
   }
   int i=0;
   while(1)
   {
       if(RPrime[i]>n)
       {
         p=RPrime[i-1];
         maxx= fact[i-1];
         break;
       }
       i++;
   }
   int indx;
   for(int i=0;i<p;i++)
   {
     n--;
     indx=Query(1,k);
     if(n==0)break;
     if(val[indx]>0)
      k=(k-1+val[indx]-1)%n+1;
     else
      k=((k-1+val[indx])%n+n)%n+1;
   }
   cout<<strname[indx]<<' '<<maxx<<endl;
  }
  return 0;
}

题意:

  N个孩子顺时针坐成一个圆圈且从1到N编号,每个孩子手中有一张标有非零整数的卡片。第K个孩子先出圈,如果他手中卡片上的数字A大于零,下一个出圈的是他左手边第A个孩子。否则,下一个出圈的是他右手边第(-A)个孩子。第p个出圈的孩子会得到F(p)个糖果,F(p)为p的因子数。求得到糖果数最多的是哪个孩子及得到多少糖果。

分析:

  出圈过程有点类似约瑟夫环。假设当前出圈的是剩余孩子中的第K个,他手中的数字为A。若A大于零,下一个出圈的就应该是剩余孩子中的第(K-1+A-1)%n+1个;若A小于零,下一个出圈的就应该是剩余孩子中的第((K-1+A)%n+n)%n+1个。问题的关键是如何求得出圈孩子的原始位置,这里我用的是线段树。线段树的每个节点的sum存储了所在区间还有多少孩子留下,查询节点rt中第num个孩子的原始位置时,如果num<=st[rt<<1].sum,则在左孩子节点中查询第num个孩子的原始位置;否则在右孩子节点中查询第num-st[rt<<1].sum个孩子的原始位置。

  另外,本题还用到了反素数。反素数的定义:对于任何正整数x,其约数的个数记做g(x)。例如g(1)=1,g(6)=4。如果某个正整数x满足:对于任意i(0<i<x),都有g(i)<g(x),则称x为反素数。对于本题,设p为不大于N的反素数,则第p个出圈的孩子得到的糖果最多,为p的约数个数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值