题目链接: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;
}