POJ-2886 Who Gets the Most Candies?(线段树+模拟)
题目大意:n个小孩按顺时针站成一圈,每次会有一个小孩出队(第一个出队的小孩已知),在他出队时会指定下一个出队的小孩,直到所有的小孩全部出队游戏结束。第p个出队的小孩会得到f(p)个糖果,f(p)为p的正约数个数。问获得最多糖果的小孩是谁?并求出他获得的糖果数。如果有多解,只输出最先出队的那个小孩。
题目分析:先将1~n之内的具有最多约数个数并且最小的那个数打出来,然后模拟相应的次数操作即可。模拟时利用到线段树,用线段树维护区间中有多少个小孩还没有出队,每次出队一个小孩就将他在树中删除,最后利用线段树查询下一个应该出队的小孩。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; # define LL long long const int N=500000; int f[N+5]; int tab[N+5]; int tr[N*4+5]; char p[N+5][12]; int next[N+5]; void init() { memset(tab,0,sizeof(tab)); for(LL i=1;i<=N;++i){ if(i*i<=N) ++tab[i*i]; for(LL j=i+1;i*j<=(LL)N;++j) tab[i*j]+=2; } f[1]=1; for(int i=2;i<=N;++i){ if(tab[i]>tab[f[i-1]]) f[i]=i; else f[i]=f[i-1]; } } void makeTree(int rt,int l,int r) { if(l==r) tr[rt]=1; else{ int mid=l+(r-l)/2; makeTree(rt<<1,l,mid); makeTree(rt<<1|1,mid+1,r); tr[rt]=tr[rt<<1]+tr[rt<<1|1]; } } void update(int rt,int l,int r,int x) { if(l==r) --tr[rt]; else{ int mid=l+(r-l)/2; if(x<=mid) update(rt<<1,l,mid,x); else update(rt<<1|1,mid+1,r,x); tr[rt]=tr[rt<<1]+tr[rt<<1|1]; } } int query1(int rt,int l,int r,int L,int R) { if(L>R) return 0; if(L<=l&&r<=R) return tr[rt]; else{ int res=0; int mid=l+(r-l)/2; if(L<=mid) res+=query1(rt<<1,l,mid,L,R); if(R>mid) res+=query1(rt<<1|1,mid+1,r,L,R); return res; } } int query2(int rt,int l,int r,int cnt) { if(l==r) return l; int mid=l+(r-l)/2; if(tr[rt<<1]>=cnt) return query2(rt<<1,l,mid,cnt); else return query2(rt<<1|1,mid+1,r,cnt-tr[rt<<1]); } int main() { init(); int n,k; while(~scanf("%d%d",&n,&k)) { for(int i=0;i<n;++i) scanf("%s%d",p[i],&next[i]); memset(tr,0,sizeof(tr)); makeTree(1,0,n-1); int now=k-1; for(int i=1;i<f[n];++i){ int l=query1(1,0,n-1,0,now-1); int r=query1(1,0,n-1,now+1,n-1); int nn; if(next[now]>0) nn=(l-1+next[now]+l+r)%(l+r); else{ next[now]=-next[now]; next[now]%=(l+r); nn=(l-next[now]+l+r)%(l+r); } update(1,0,n-1,now); now=query2(1,0,n-1,nn+1); } printf("%s %d\n",p[now],tab[f[n]]); } return 0; }