[POJ2886]Who Gets the Most Candies?
题目:Who Gets the Most Candies?
链接:http://poj.org/problem?id=2886
分析:
1)这是一个约瑟夫环,关键在于如何定位下一个位置。如同楼层没有0层一样,针对card需要分正负讨论,计算出在新环中下一个离开的位置k。
2)求出k在原环中的位置。采用数据结构维护一下。
3)更新答案,移除小朋友,计算新的位置,直到结束。
4)求反素数,用类似筛法的方式计算一下。当然也可以打表。
5)有一个小优化,求出最优解所需的步数,就不用等到到全部小朋友都离开啦。
方法一:二分+树状数组
二分位置mid,树状数组维护 位置mid前 还有多少小朋友在现在的约瑟夫环中存在。
更新答案并在树状数组中移除这个小朋友。
#include <cmath> #include <cstdio> #include <iostream> using namespace std; const int maxN=500005; char a[maxN+5][10]; int n,b[maxN+5],f[maxN+5],T[maxN+5]; void Tadd(int x,int y){for(int i=x;i<=n;i+=i&-i)T[i]+=y;} int Tque(int x){int ret=0;for(int i=x;i;i-=i&-i)ret+=T[i];return ret;} void Init(){ for(int i=1,k=(int)(sqrt(double(maxN)+0.5));i<=k;++i){ for(int j=i+1;j*i<=maxN;++j)f[j*i]+=2; ++f[i*i]; } } int main(){ Init(); for(int k,ans0,ans1;~scanf("%d%d",&n,&k);){ for(int i=1;i<=n;++i)T[i]=0; for(int i=1;i<=n;++i){scanf("%s %d",a[i],&b[i]);} for(int i=1;i<=n;++i)Tadd(i,1); ans1=0; for(int num=n,t=0;1;){ int l=1,r=n,mid; for(;l<=r;){ mid=(l+r)>>1; if(Tque(mid)<k)l=mid+1;else r=mid-1; } Tadd(l,-1); ++t; if(f[t]>ans1){ans0=l;ans1=f[t];} --num; if(!num)break; if(b[l]>0){ k=((k-2+num)%num+b[l])%num+1; }else{ k=((k+b[l]-1)%num+num)%num+1; } } printf("%s %d\n",a[ans0],ans1); } return 0; }
方法二:线段树
每个节点维护这个区间有多少小朋友,在线段树中寻找第k个小朋友的位置pos。
更新答案并在线段树中移除这个小朋友。
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxN=500005; char a[maxN+5][10]; int n,pos,b[maxN+5],f[maxN+5]; struct SegmentTree{ int val[maxN<<2]; void init(int v,int l,int r){ if(l==r){val[v]=1;return;} int mid=(l+r)>>1; init(v<<1,l,mid);init((v<<1)+1,mid+1,r); val[v]=val[v<<1]+val[(v<<1)+1]; } void Modify(int v,int l,int r,const int &k){ if(l==r){--val[v];pos=l;return;} int mid=(l+r)>>1; if(k<=val[v<<1])Modify(v<<1,l,mid,k);else Modify((v<<1)+1,mid+1,r,k-val[v<<1]); --val[v]; } }T; void Init(){ for(int i=1,k=(int)(sqrt(double(maxN)+0.5));i<=k;++i){ for(int j=i+1;j*i<=maxN;++j)f[j*i]+=2; ++f[i*i]; } } int main(){ Init(); for(int k,ans0,ans1;~scanf("%d%d",&n,&k);){ memset(T.val,0,sizeof T.val); for(int i=1;i<=n;++i){scanf("%s %d",a[i],&b[i]);} T.init(1,1,n); ans1=0; for(int t=0,num;1;){ T.Modify(1,1,n,k); ++t; if(f[t]>ans1){ans0=pos;ans1=f[t];} num=T.val[1]; if(!num)break; if(b[pos]>0){ k=((k-2+num)%num+b[pos])%num+1; }else{ k=((k+b[pos]-1)%num+num)%num+1; } } printf("%s %d\n",a[ans0],ans1); } return 0; }
PS:反素数表:
int max_turn[40]={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 max_candy[40]={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};