wikio p1282 约瑟夫问题

来源:互联网 发布:淘宝开店介绍怎么写 编辑:程序博客网 时间:2024/04/29 21:58

首先这道题目的数据规模还是很大的,而且数据也比较强大的。

所以这题需要用到线段树。

首先建立线段树,建立孩子信息,以及该子树未出局的小朋友数量。

我们建立好之后,可以add一下,计算还有多少孩子没有出局(当然如果通过循环,N-i+1就是剩余的孩子数量)

把M%=N-i+1

这样可以做到优化,如果空循环的话不仅没有用而且可能会导致超时。

这个时候我们应该有一个上一个报到的孩子编号,然后然后判断一下下一个报M的孩子是在1~上一个孩子的编号i之间,还是在i+1~N之间

当搜索到[s,t]这个区间的时候,判断一下是在左孩子的区间,还是在右孩子的区间,当然如果在右区间当中,应该把报到的编号加上左区间的孩子剩余数量。

题目还是很简单的。

当然数据强大,要不然谁会这么无聊用线段树。

 

#include<stdio.h>#include<iostream>#include<memory.h>using namespace std;const int MAX_N = 30001;const int root = 1;int N,M,P;struct node{int left,right;int data;}T[MAX_N*10];int ans[MAX_N];int BuildTree(int t,int l,int r){if (l>r) return 0;if (l==r){T[t].left=l;T[t].right=r;T[t].data=1;return T[t].data;}int mid=(l+r)/2;T[t].left=l;T[t].right=r;return T[t].data=BuildTree(t*2,l,mid)+BuildTree(t*2+1,mid+1,r);}int init(){scanf("%d %d",&N,&M);BuildTree(root,1,N);}int add(int t,int l,int r){if (l>r) return 0;if (l==T[t].left&&r==T[t].right) return T[t].data;int mid=(T[t].left+T[t].right)/2;if (r<=mid) return add(t*2,l,r);if (l>mid) return add(t*2+1,l,r);return add(t*2,l,mid)+add(t*2+1,mid+1,r); }int next(int t){if (t==N) return 1;else return t+1;}int Tree_Delete(int t,int v){if (T[t].left==T[t].right) return T[t].data=0;int mid=(T[t].left+T[t].right)/2;T[t].data--;if (v<=mid) return Tree_Delete(t*2,v);if (v>mid) return Tree_Delete(t*2+1,v);}int search(int t,int l,int r,int p){if (T[t].left==T[t].right) return T[t].left;int mid=(T[t].left+T[t].right)/2;if (r<=mid) return search(t*2,l,r,p);if (l>mid) return search(t*2+1,l,r,p);if (add(t*2,l,mid)>=p) return search(t*2,l,mid,p);else return search(t*2+1,mid+1,r,p-add(t*2,l,mid));}int work(){int i;for (i=1;i<=N;i++){P=(M-1)%(N-i+1)+1;if (add(root,ans[i-1]+1,N)>=P)ans[i]=search(root,ans[i-1]+1,N,P);elseans[i]=search(root,1,ans[i-1],P-add(root,ans[i-1]+1,N));Tree_Delete(root,ans[i]);}}int put(){int i;for (i=1;i<=N;i++)printf("%d ",ans[i]);}int main(){init();work();put();return 0;}

原创粉丝点击