模拟:
STL队列:
#include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #define inf 0x3f3f3f3f #define LL unsigned long long #include<queue> using namespace std; int n,m; queue<int> q; /******Program Begin*********/ int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)q.push(i); int cnt=0; while(!q.empty()) { int t = q.front(); q.pop(); cnt++; if(cnt%m==0) { cnt=0; printf("%d ",t); continue; } else q.push(t); } return 0; } /*******Program End**********/
#include"stdio.h" int main() { int m,n,i,j; int s=1; int a[30005]; scanf("%d%d",&n,&m); for(i=0;i<=n;i++) <span style="white-space:pre"> </span>a[i]=i; while(n){ i=(s+m-1)%n;//出列的下标 if(i==0) i=n; printf("%d ",a[i]); for(j=i;j<n;j++)//删除出列的 a[j]=a[j+1]; n--; s=i;//下一个开始数的位置 } return 0; }
先把所有1~n的编号全都插入一棵二叉树(别顺序插入,可以随机序也可以分治递归),表示当前状态存活的人的集合,并维护每棵子树的节点数size
关键是要删除一个点后如何快速找到下次该删哪个点
我们可以维护当前存活的人数,删除的时候也需要搜索一并求出这个点在二叉树中的rank值,很容易就可以找出下一个要删掉点的rank值
根据这个rank值删除下一个点,这样做n遍
由于只有删除操作普通二叉树就够了(不会退化),不过要是闲的话写个平衡的也没问题
个人认为块链也可以,并且更易实现
#include<cstdio> #include<iostream> using namespace std; struct tre{ int a,b,s;//a表示左边界, b表示右边界 ,s表示当前节点下方有多少个叶子节点。 }tree[100000];//必须开1000000,线段树一般需要n*3的空间 int n,m; void change(int x){ tree[x].s=tree[x*2].s+tree[x*2+1].s; } void buildtree(int a,int b,int x){//建设线段树,x为tree中编号 tree[x].a=a; tree[x].b=b;//左右边界赋值 if (a==b) { tree[x].s=1; return; }//叶子节点返回值 int mid=(a+b)/2; buildtree(a,mid,x*2); buildtree(mid+1,b,x*2+1);//拆分成两段 change(x);//算出当前节点的s权值 } int addtree(int t,int x){//删叶子,t指tree中序号,x指要删除的叶子,前面应该有几个叶子。 if (tree[t].a==tree[t].b){ tree[t].s=0; return tree[t].a; }//找到了要被剔除的叶子节点 int temp; if (tree[2*t].s>=x) temp=addtree(2*t,x);//如果不是叶子,往两侧找 else temp=addtree(2*t+1,x-tree[2*t].s); change(t); return temp;//返回那个被删除的叶子 } int main(){ cin>>n>>m; int cur=1,x=0,left=n; buildtree(1,n,1); for (int i=1;i<=n;i++){ x=(cur+m-1) % left;//计算从1开始数之前有多少个叶子, if (0==x) x=left;//特别注意,模出0就意味着数到头 cur=x; left--; cout<<addtree(1,cur)<<" "; } return 0; }
var n,m,i,place:longint; a:array[1..30000]of longint; begin read(n,m); for i:=1 to n do a[i]:=i; place:=0; for i:=n downto 2 do begin inc(place,m); while place>i do dec(place,i); write(a[place],' '); move(a[place+1],a[place],(i-place)<<2);//Pascal传说中的block操作 dec(place); end; writeln(a[1]); end.
线段树,模拟每次离队的是场上第几个 #include<stdio.h> int n,m,k,v; struct Trees{ int l,r,sum; }t[120001]; void Get_tree(int L,int R,int k) { t[k].l=L; t[k].r=R; t[k].sum=R-L+1; if(L==R) return; Get_tree(L,(L+R)>>1,k<<1); Get_tree(((L+R)>>1)+1,R,(k<<1)+1); } void Move(int k,int w) { t[k].sum--; if(t[k].l==t[k].r){ printf("%d ",t[k].l); return; } if(t[k<<1].sum>=w) Move(k<<1,w); else Move((k<<1)+1,w-t[k<<1].sum); } main() { scanf("%d %d",&n,&m); Get_tree(1,n,1); k=n; while(k) { v=(v-1+m)%k; Move(1,v+1); k--; } }