题目描述 Description
有编号从1到N的N个小朋友在玩一种出圈的游戏。开始时N个小朋友围成一圈,编号为I+1的小朋友站在编号为I小朋友左边。编号为1的小朋友站在编号为N的小朋友左边。首先编号为1的小朋友开始报数,接着站在左边的小朋友顺序报数,直到数到某个数字M时就出圈。直到只剩下1个小朋友,则游戏完毕。
现在给定N,M,求N个小朋友的出圈顺序。
输入描述 Input Description
唯一的一行包含两个整数N,M。(1<=N,M<=30000)
输出描述 Output Description
唯一的一行包含N个整数,每两个整数中间用空格隔开,第I个整数表示第I个出圈的小朋友的编号。
样例输入 Sample Input
5 3
样例输出 Sample Output
3 1 5 2 4
比较水的一道题,刚开始以为必须要用线段树做,特地去看了网上的题解。
结果打完了发现学长们的AC代码才20多行(╯`□′)╯︵┻━┻ 果然是codevs上的数据太水了???
总之正解的思想就是每次找出当前要删的数在已经删去上一个数的序列里的(相对)位置,如 1,2,3,4,5, n=2,先删a[2],则 1,2,3,4,5 中下一个要删的数4在如今的数列(1,3,4,5)里的位置是 3 (注意每次删数后,都要把之后的数往前挪一位),则再删a[3]。
至于代码我就不打了,只放线段树的 (*◎v◎*)
nlogn
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int N,M,step=1;
struct maple{
int l,r,sum;
}Tree[30010<<2];
void Build(int n,int l,int r)
{
Tree[n].l=l,Tree[n].r=r;
if(l==r)
{
Tree[n].sum=1;
return ;
}
int mid=(l+r)>>1;
Build(n<<1,l,mid);
Build(n<<1|1,mid+1,r);
Tree[n].sum=Tree[n<<1].sum+Tree[n<<1|1].sum;
}
int Find(int n,int k)
{
--Tree[n].sum;
if(Tree[n].l==Tree[n].r) return Tree[n].r;
if(k<=Tree[n<<1].sum) return Find(n<<1,k);
else return Find(n<<1|1,k-Tree[n<<1].sum);
}
int main()
{
scanf("%d%d",&N,&M);
Build(1,1,N);
for(int i=1;i<=N;++i)
{
step=(step+M-1)%Tree[1].sum; //要删的数在序列里的相对位置,以一为首
if(step==0) step=Tree[1].sum; //
cout<<Find(1,step)<<" ";
}
return 0;
}
PS:( ‘-ωก̀ )好困。。。