wikio p1282 约瑟夫问题

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

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

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

我们建立好之后,可以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);
		else
		ans[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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值