zoukankan      html  css  js  c++  java
  • poj 2886 "Who Gets The Most Candies?"(树状数组)

    传送门

    参考资料:

      [1]:http://www.hankcs.com/program/algorithm/poj-2886-who-gets-the-most-candies.html

    题意:

      抢糖:N个熊孩子围成一个圈,从第K个开始淘汰,每淘汰一个,出示手中的数字,决定下一个淘汰者,正数表示左手第n个,负数反之。每个人可以拿到的存活回数的因数个数的糖果,求拿到最多糖果数的孩子的名字以及糖果数。

      (以上题意来自参考资料[1])

    下面谈谈我对参考资料[1]的理解:

      下面介绍一下相对位置的概念(自己理解的,表达欠缺还请留言告知):

      假设初始有 6 个人,编号分别为 1~6

      1  2  3  4  5  6(初始编号)

      1  2  3  4  5  6(相对位置)

      初始编号为2的小盆友离开后,相对位置变为:

      1  3  4  5  6(初始编号)

      1  2  3  4  5(相对位置)

      那么,首先考虑这个问题,如何根据当前离开的小盆友的初始编号 k 和其手中的卡片 x 确定下一个小盆友离开的相对位置?

      分两种情况:

      假设当前还有 curTot 个小盆友,k 位置前有 before 个小盆友,k 位置后有 after 个小盆友(before + after = curTot);

      ① x > 0 

        从 k 位置向右找第 x 个小盆友,相当于从第一个位置的小盆友向右找第 nex=(before+x)%curTot 个小盆友;

        因为第一个小盆友的相对位置为 1,所以,需要查找的第 nex 个小盆友的相对位置为 nex+1;

      ② x < 0

        从第 k 位置向左找第 x 个小盆友,相当于从第一个位置的小盆友向左找第 nex=(after+x)%curTot 个小盆友;

        当前一共有 curTot 个小盆友,那,相当于从第一个位置的小盆友向右找第 nex= (curTot-nex)%curTot 个小盆友;

        因为第一个小盆友的相对位置为 1,所以,需要查找的第 nex 个小盆友的相对位置为 nex+1;

      相对位置求出来后,如何求解其对应的初始编号呢???

      引用参考资料[1]博主的话就是 “这种区域修改可以活用BIT高效完成”;

      怎么个活用法呢?

      首先看一下BIT代码:

     1 struct BIT
     2 {
     3     int bit[maxn];
     4     void Init()
     5     {
     6         mem(bit,0);
     7     }
     8     void Add(int t,int x)
     9     {
    10         while(t < maxn)
    11         {
    12             bit[t] += x;
    13             t += lowbit(t);
    14         }
    15     }
    16     int Sum(int t)
    17     {
    18         int sum=0;
    19         while(t > 0)
    20         {
    21             sum += bit[t];
    22             t -= lowbit(t);
    23         }
    24         return sum;
    25     }
    26 }_bit;

      初始,将 1~n 个小盆友全加入到BIT中;

    for(int i=1;i <= n;++i)
        _bit.Add(i,1);

      那么,此时初始编号 i 对应的前缀和Sum(i)正好为 i 的相对位置:

              i : 1  2  3  4  5  6

      _bit.Sum(i) : 1  2  3  4  5  6

      假设初始 i = 2 小盆友出队,调用_bit.Add(2,-1)

              i : 1  2  3  4  5  6

      _bit.Sum(i) : 1  1  2  3  3  4

      如果我现在想知道相对位置为3的小盆友的初始编号,该怎么办呢?

      你会发现,_bit.Sum(4) = 3,4正好是2好小盆友出队后相对位置为3的小盆友的编号;

      那么,想要找相对位置为 pos 的初始编号,只需在_bit.Sum()中找第一个使 _bit.Sun(No) = pos 的No即可;

      因为 _bit.Sum() 有序,所以,在查找的时候可以二分查找以提高效率;

    AC代码:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 using namespace std;
      5 #define lowbit(x) (x&-x)
      6 #define mem(a,b) memset(a,b,sizeof(a))
      7 const int maxn=5e5+50;
      8 
      9 int n,k;
     10 char name[maxn][15];
     11 int x[maxn];
     12 int factor[maxn];
     13 struct BIT
     14 {
     15     int bit[maxn];
     16     void Init()
     17     {
     18         mem(bit,0);
     19     }
     20     void Add(int t,int x)
     21     {
     22         while(t < maxn)
     23         {
     24             bit[t] += x;
     25             t += lowbit(t);
     26         }
     27     }
     28     int Sum(int t)
     29     {
     30         int sum=0;
     31         while(t > 0)
     32         {
     33             sum += bit[t];
     34             t -= lowbit(t);
     35         }
     36         return sum;
     37     }
     38     int BS(int x)
     39     {
     40         int l=0,r=n+1;
     41         while(r-l > 1)
     42         {
     43             int mid=l+((r-l)>>1);
     44             if(Sum(mid) >= x)
     45                 r=mid;
     46             else
     47                 l=mid;
     48         }
     49         return r;//使Sum()=x 的第一个位置
     50     }
     51 }_bit;
     52 void factorTable()
     53 {
     54     fill(factor,factor+maxn,1);
     55     for(int i=2;i < maxn;++i)
     56     {
     57         if(factor[i] != 1)
     58             continue;
     59         for(int j=i;j <= maxn;j+=i)
     60         {
     61             int k=0;
     62             for(int m=j;m%i == 0;m/=i,k++);
     63             factor[j] *= k+1;//j包含k个质数i
     64         }
     65     }
     66 }
     67 void Solve()
     68 {
     69     int candy=0;
     70     int index;
     71     for(int i=1;i <= n;++i)
     72     {
     73         if(candy < factor[i])
     74         {
     75             candy=factor[i];//更新candy
     76             index=k;//第i个出队的编号是k(初始编号)
     77         }
     78         if(i == n)
     79             break;
     80 
     81         _bit.Add(k,-1);
     82         int nex;
     83         int curTot=n-i;//当前剩余小盆友的个数
     84         if(x[k] > 0)//情况①
     85         {
     86             nex=(x[k]+_bit.Sum(k)-1)%curTot;
     87             nex++;
     88         }
     89         else//情况②
     90         {
     91             x[k]=-x[k];
     92             nex=(x[k]+curTot-_bit.Sum(k))%curTot;//向左找第nex个
     93             nex=(curTot-nex)%curTot;//转化为向右找
     94             nex++;
     95         }
     96         k=_bit.BS(nex);//二分查找相对位置为nex的初始编号
     97     }
     98     printf("%s %d
    ",name[index],candy);
     99 }
    100 int main()
    101 {
    102     factorTable();//预处理出因子表
    103     while(~scanf("%d%d",&n,&k))
    104     {
    105         _bit.Init();
    106         for(int i=1;i <= n;++i)
    107         {
    108             scanf("%s%d",name[i],x+i);
    109             _bit.Add(i,1);
    110         }
    111         Solve();
    112     }
    113     return 0;
    114 }
    View Code

    其实,这道题被我搁置四天了,当时按照自己的思路写了个线段树的,wa了;

    改了许久无果,无奈,搜了搜题解,第一个题解看的就是参考资料[1],

    不过,当时没怎么理解,又找了找其他人的博客,学到了一个新概念--反素数(可以提前打出反素数表,也可以提前打出因子表);

    这几天,训练赛几乎每天都有,忙着训练,忙着补题;

    这道题只是在我闲暇时光看看,捋捋思路;

    昨天晚上终于有点懵懂;

    今天,又花费了一个多小时,终于AC了,不过,和[1]博主的代码不太一样,[1]博主的代码我还没怎么,理解透呢,只是有点懵懂;

    或许,这就是大佬吧,写出的代码得让吾等蒟蒻看好久才能理解Orz

  • 相关阅读:
    NHibernate 配置增加代码感知
    NHibernate应用开发
    Spring.Net+NHibernate+Castle学习网站
    Windows Live Writer 网易博客配置
    第一章. 序言
    NHibernate之配置文件属性说明
    Log4Net各参数API
    EntityFramework 6.0< Code First > 连接 Mysql数据库
    maven阿里云中央仓库
    eclipse安装maven
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10751154.html
Copyright © 2011-2022 走看看