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;
}
浙公网安备 33010602011771号