Wiki OI 1282 约瑟夫问题

题目链接:http://wikioi.com/problem/1282/

算法与思路:线段树;

这个题目如果使用链表模拟出圈,时间复杂度为O(m*n);题目的数据规模是3W,

显然会超时(当然如果数据水就不好说了);使用线段树的话却可以达到O(nlogn);

首先递归建树,模版就不解释了;然后求相对位置seq,表示出圈者是当前圈中

剩余者的第seq位;然后根据相对位置寻找seq的实际编号,如果seq不大于左子树的

节点数,则在左子树中搜寻,否则将seq减去左子树的节点数,并在右子树中搜寻,

如此递归,直到找到叶子节点,叶子节点的指就是seq最初的编号。

代码实现:

#include<stdio.h>
#define N 30005
#define lson l, mid, root<<1
#define rson mid + 1, r, root<<1|1
int sum[N<<2];   //sum[i]指编号为i的根节点的子节点个数 
int tree[N<<2][2];
void PushUp(int root)  //向上更新 
{
	sum[root] = sum[root<<1] + sum[root<<1|1];
} 
void build(int l, int r, int root)  //递归建树 
{
	tree[root][0] = l;
	tree[root][1] = r;
	if(l == r)  //判断是否为叶子节点 
	{
		sum[root] = 1;   
		return ;
	}
	int mid = (l + r) >> 1;
	build(lson);
	build(rson);
	PushUp(root);
}
int update(int p, int root)
{
	sum[root]--;   //管辖范围包括p的根节点的子节点数减一 
	if(tree[root][0] == tree[root][1])
	{
		sum[root] = 0;
		return tree[root][0];	
	}
	if(p <= sum[root<<1])  //寻找p的绝对位置,即圈中第p个人的编号 
	   return update(p, root<<1);
	else
		return update(p - sum[root<<1], root<<1|1);
	PushUp(root); 
}
int main()
{
	int n, m;
	scanf("%d%d", &n, &m); 
	{
		build(1, n, 1);
		int pos = 1;
		int seq = 1;
		for(int i = 0; i < n; i++)
		{
			seq = (seq + m - 1) % sum[1];  //seq指出圈者在剩余者中的位置 
			if(!seq)
			    seq = sum[1];
			pos = update(seq, 1);
			printf("%d ", pos);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值