http://poj.org/problem?id=2886
这个题和插队买票那题一样 http://www.cnblogs.com/liulangye/archive/2012/06/11/2545529.html
题目大意:
一些人围成一圈第一个人跳出圈后会告诉你下一个谁跳出来
跳出了的人所得到的糖果数量和他跳出的顺序有关
线段树在这里只是一个二分搜索的作用
思路:
1.求第几个跳出者所得糖果数量
2.根据输入范围,求的第几个人是得到糖果最多的 假设是J
3.建立线段树,线段树区间表示区间内人的个数,
搜索第几个人时所经过的路径区间人数的减一
然后根据提示求的下一个跳出的人是谁 并记录第J个人是谁
代码及其注释:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<queue> #include<algorithm> #include<vector> using namespace std; const int N=500005; struct node { int l,r; int sum; }mem[N*3]; struct node1 { char name[12]; int a; }step[N];//输入名字 及其下一个跳出提示 int candysum[N];//第i个人跳出所得糖果 int ansi;//得糖果最多的人的输入序号 int I,n,c,J;//J表示第几个跳出的人得到的糖果最多 void ready()//求第i人跳出最得到的糖果数量 { memset(candysum,0,sizeof(candysum)); for(int i=1;i<N;++i) { ++candysum[i]; for(int j=i*2;j<N;j=j+i) { ++candysum[j]; } } } void build(int x,int l,int r)//建树 { mem[x].l=l; mem[x].r=r; mem[x].sum=(r-l+1); if(mem[x].l==mem[x].r) return; int mid=(r+l)>>1; build(x*2,l,mid); build(x*2+1,mid+1,r); } void jump(int x,int s)//s表示目前队列此区间第几个人跳出 { --mem[x].sum;//所到区间人数减一 if(mem[x].l==mem[x].r)//最后结点 { if(J==c) { ansi=mem[x].l;//如果是的J个人跳出 记录答案 } if(n-c==0)//全跳出 return ; if(step[mem[x].l].a>0)//以下是求出目前队列从线段树左起下一次第几个人跳出 --I; I=((I+step[mem[x].l].a)%(n-c)+(n-c))%(n-c); if(I==0) I=n-c; } else { if(mem[x*2].sum>=s)//左边人数足够则向左搜 { jump(x*2,s); } else { s-=mem[x*2].sum;//否则减去左边人数向右搜 jump(x*2+1,s); } } } int main() { int k; ready(); while(scanf("%d %d",&n,&k)!=EOF) { J=1; for(int i=1;i<=n;++i) { getchar(); scanf("%s %d",step[i].name,&step[i].a); if(candysum[i]>candysum[J]) J=i; } build(1,1,n); I=k; for(c=1;c<=n;++c) { jump(1,I); } printf("%s %d\n",step[ansi].name,candysum[J]); } return 0; }