首页 > 代码库 > POJ 2828 Buy Tickets 线段树解法
POJ 2828 Buy Tickets 线段树解法
此题应用线段树的方法非常巧妙。没做过真的难想得出是这么想的。
是一个逆向思维的运用。
其实一看到这道题目我就想到要运用逆向思维的了,但是就是没那么容易想通的。
思路:
1 要从后面往前更新线段树
2 线段树记录的是当前区间还剩下多少个记录空间
3 因为后面的插入会使得前面的插入往后退,那么前面插入的下标如果大于前面可以插入的空间,就需要往后退了。
好难理解的操作。仔细观察一下下面update这个函数吧。
二分地去查找适合的插入位置,把插入操作时间效率提高到 O(lgn),如果使用一般方法插入操作效率就会达到O(n)。
const int SIZE = 200001; const int TREESIZE = SIZE + (SIZE<<1); int segTree[TREESIZE]; int pos[SIZE]; int val[SIZE]; int orderVal[SIZE]; inline int lChild(int rt) { return rt<<1; } inline int rChild(int rt) { return rt<<1 | 1; } void build(int l, int r, int rt) { segTree[rt] = r - l + 1; //记录有多少个存储空间,以解决位置冲突 if (l == r) return ; int m = l + ((r-l)>>1); build(l, m, lChild(rt)); build(m+1, r, rChild(rt)); } void update(int i, int p, int l, int r, int rt) { segTree[rt]--; if (l == r) { orderVal[l] = val[i];//l为适当位置填写val return ; } int m = l + ((r-l)>>1); if (segTree[lChild(rt)] >= p) update(i, p, l, m, lChild(rt)); //下面是精要的地方,十分巧妙的插队法。如果后面有人插队了,那么前面的人和前面插队的人都需要往后推 else update(i, p - segTree[lChild(rt)], m+1, r, rChild(rt)); } int main() { int n; while(scanf("%d", &n) == 1) { build(1, n, 1); for (int i = 1; i <= n; i++) { scanf("%d %d", &pos[i], &val[i]); } for (int i = n; i > 0; i--) { update(i, pos[i]+1, 1, n, 1); } for (int i = 1; i < n; i++) { printf("%d ", orderVal[i]); } printf("%d\n", orderVal[n]); } return 0; }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。