zoukankan      html  css  js  c++  java
  • poj 2886 Who Gets the Most Candies?

    题意:n个人围城一圈,每个人决定下一个出局的人在他的第几个位置,首先出局的人是第k个人
    分析:反素数+约瑟夫

    这道题最主要需要理解的就是线段树是如何模拟的反素数,sum数组记录的是队列中还剩余多少个人
    k表示的是在剩余里的人,其排在第几个,通过对线段树的询问找到该第k个人在初始队列中排第几个
    若k<=sum[rt<<1]则进入到其左子节点,否则的话,k-sum[rt<<1|1]进入到右子节点

    还有一个难点就是,剩余中k位置的更新,当next[id]>0的时候,在k-1位置的人已经被删除了,所以应该
    在加上next[id]后还应该减去1,同时每次取余的时候t都已经减1了,k在加上1表示的第几个人
    next[id]<0
                   k = ((k-1+next[id])%t+t)%t+1;
    next[id]>=0
                   k = (k-1+next[id]-1)%t+1;    

    #include<stdio.h>
    #include<string.h>
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    const int MN= 500100;
    int sum[MN<<2];
    int next[MN];
    char nam[MN][20];
    
    int a[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 b[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};
    
    void PushUP(int rt)
    {
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
    
    void build(int l,int r,int rt)
    {
        if(l==r)
        {
            sum[rt]=1;
            return ;
        }
        int m=(l+r)>>1;
        build(lson);
        build(rson);
        PushUP(rt);
    }
    
    int query(int k,int l,int r,int rt)
    {
        if(l==r)
        {
            sum[rt]--;
            return l;
        }
        int m=(l+r)>>1;
        int ret;
        if(k<=sum[rt<<1]) ret=query(k,lson);
        else
        {
            k-=sum[rt<<1];
            ret=query(k,rson);
        }
        PushUP(rt);
        return ret;
    }
    
    int main()
    {
        int i,n,k;
        while(scanf("%d%d",&n,&k)!=EOF)
        {
            for(i=0;i<n;i++)
              scanf("%s%d",nam[i],&next[i]);
            build(0,n-1,1);
            i=0;
            while(a[i]<=n) i++;
            int p=a[i-1];
            int MAX=b[i-1];
            int id;
            int t=n;
            for(i=0;i<p;i++)
            {
                t--;
                id=query(k,0,n-1,1);
                //id表示的是初始的位置在哪
                if(t==0) break;
                if(next[id]<0)//向前
                {
                   k = ((k-1+next[id])%t+t)%t+1;
                }
                else//向后
                {//k表示的剩余人中的第几个
                   k = (k-1+next[id]-1)%t+1;
                }
            }
            printf("%s %d\n",nam[id],MAX);
        }
        return 0;
    }

     

  • 相关阅读:
    VBoxManage命令详解
    十条nmap常用的扫描命令
    2015-12-16 第八天笔记整理-第二部分
    2015-12-13 第八天笔记整理-第一部分
    2015-12-06 第七天课程笔记
    2015-12-04 学习笔记整理
    2015-11-22 第五天
    选择控制语句和循环结构
    数据类型和运算符
    常用DOS指令
  • 原文地址:https://www.cnblogs.com/zsboy/p/3112526.html
Copyright © 2011-2022 走看看