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