传送门
参考资料:
[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 #include2 #include 3 #include 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\n",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 }
其实,这道题被我搁置四天了,当时按照自己的思路写了个线段树的,wa了;
改了许久无果,无奈,搜了搜题解,第一个题解看的就是参考资料[1],
不过,当时没怎么理解,又找了找其他人的博客,学到了一个新概念--反素数(可以提前打出反素数表,也可以提前打出因子表);
这几天,训练赛几乎每天都有,忙着训练,忙着补题;
这道题只是在我闲暇时光看看,捋捋思路;
昨天晚上终于有点懵懂;
今天,又花费了一个多小时,终于AC了,不过,和[1]博主的代码不太一样,[1]博主的代码我还没怎么,理解透呢,只是有点懵懂;
或许,这就是大佬吧,写出的代码得让吾等蒟蒻看好久才能理解Orz