POJ 2828 Buy Tickets(神题!线段树or树状数组)

题目链接:POJ
2828 Buy Tickets

【题意】给了你
n(1<=n<=200000)个人的插队信息,让你输出最终的队列的排列

【思路】常规思考的话,这道题就是模拟,但是时间复杂度一定会很高。POJ的discuss上说这道题是神题,难得不是用什么数据结构,而是思路,这道题要逆向去思考,从最后一个人往前看,后插进来的人更容易定位,他一定能站到他想的位置,并且不会在挪动。再前一个人呢?他的位置即是接下来的空位的编号。于是有线段树用于维护位置信息。当然用树状数组也是可以做的,但是要加上二分来优化,也可以过。

下面是线段树代码:

 1 /*
2 ** POJ 2828 Buy Tickets
3 ** Created by Rayn @@ 2014/05/08
4 ** 神奇线段树,奇妙的逆向思维
5 */
6 #include <cstdio>
7 #include <cstring>
8 #include <algorithm>
9 using namespace std;
10 const int MAX = 200005;
11
12 int pos[MAX], val[MAX], ans[MAX];
13 int tree[MAX*3], index;
14
15 void Build(int k, int l, int r)
16 {
17 tree[k] = r - l + 1; //开始时每个节点都有空位
18 if(l == r)
19 return ;
20 int mid = (l + r) >> 1;
21 Build(k<<1, l, mid);
22 Build(k<<1|1, mid+1, r);
23 }
24 void Update(int k, int l, int r, int pos)
25 {
26 tree[k]--; //单点更新,减少一个空位
27 if(l == r)
28 {
29 index = l;
30 return ;
31 }
32 int mid = (l + r) >> 1;
33 if(tree[k<<1] >= pos) //如果当前位置的左边空位够的话就落在左边
34 {
35 Update(k<<1, l, mid, pos);
36 }
37 else
38 {
39 pos -= tree[k<<1]; //否则,就把左边的空位减去,再在右边定位
40 Update(k<<1|1, mid+1, r, pos);
41 }
42 }
43 int main()
44 {
45 #ifdef _Rayn
46 freopen("in.txt", "r",stdin);
47 #endif
48 int n;
49 while(scanf("%d", &n) != EOF)
50 {
51 Build(1, 1, n);
52 for(int i=1; i<=n; ++i)
53 {
54 scanf("%d%d", &pos[i], &val[i]);
55 }
56 for(int i=n; i>=1; --i)
57 {
58 Update(1, 1, n, pos[i]+1);
59 ans[index] = val[i];
60 }
61 for(int i=1; i<=n; ++i)
62 {
63 printf("%d%c", ans[i], (i == n)? ‘\n‘:‘ ‘);
64 }
65 }
66 return 0;
67 }

树状数组+二分的代码:

 1 /*
2 ** POJ 2828 Buy Tickets
3 ** Created by Rayn @@ 2014/05/08
4 ** 树状数组+二分
5 */
6 #include <cstdio>
7 #include <cstring>
8 #include <algorithm>
9 using namespace std;
10 const int MAX = 200005;
11
12 int n, pos[MAX], val[MAX];
13 int tree[MAX], ans[MAX];
14
15 inline int Lowbit(int x)
16 {
17 return x&(-x);
18 }
19 int GetSum(int idx)
20 {
21 int sum = 0;
22 while(idx > 0)
23 {
24 sum += tree[idx];
25 idx -= Lowbit(idx);
26 }
27 return sum;
28 }
29 void Update(int idx, int val)
30 {
31 while(idx <= n)
32 {
33 tree[idx] += val;
34 idx += Lowbit(idx);
35 }
36 }
37 int Search(int left, int right, int pos)
38 {
39 while(left <= right)
40 {
41 int mid = (left + right) >> 1;
42 int tmp = GetSum(mid);
43 if(tmp == pos && ans[mid] == -1)
44 return mid;
45 if(tmp >= pos)
46 right = mid - 1;
47 else
48 left = mid + 1;
49 }
50 return -1;
51 }
52 int main()
53 {
54 #ifdef _Rayn
55 freopen("in.txt", "r",stdin);
56 #endif
57
58 while(scanf("%d", &n) != EOF)
59 {
60 memset(tree, 0, sizeof(tree));
61 memset(ans, -1, sizeof(ans));
62 for(int i=1; i<=n; ++i)
63 {
64 scanf("%d%d", &pos[i], &val[i]);
65 pos[i]++;
66 Update(i, 1); //所有位置填充1,代表有空位
67 }
68 for(int i=n; i>=1; --i)
69 {
70 int index = Search(pos[i], n, pos[i]);
71 ans[index] = val[i];
72 Update(index, -1);
73 }
74 for(int i=1; i<=n; ++i)
75 {
76 printf("%d%c", ans[i], (i == n)? ‘\n‘:‘ ‘);
77 }
78 }
79 return 0;
80 }

时间: 2024-10-11 05:21:04

POJ 2828 Buy Tickets(神题!线段树or树状数组)的相关文章

poj 2828 Buy Tickets 万能的线段树大法。

Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 14400   Accepted: 7199 Description Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue- The Lunar New Year wa

(中等) POJ 2828 Buy Tickets , 逆序+线段树。

Description: Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue… The Lunar New Year was approaching, but unluckily the Little Cat still had schedules going here and there. Now, he h

POJ 2828 Buy Tickets 线段树解法

此题应用线段树的方法非常巧妙.没做过真的难想得出是这么想的. 是一个逆向思维的运用. 其实一看到这道题目我就想到要运用逆向思维的了,但是就是没那么容易想通的. 思路: 1 要从后面往前更新线段树 2 线段树记录的是当前区间还剩下多少个记录空间 3 因为后面的插入会使得前面的插入往后退,那么前面插入的下标如果大于前面可以插入的空间,就需要往后退了. 好难理解的操作.仔细观察一下下面update这个函数吧. 二分地去查找适合的插入位置,把插入操作时间效率提高到 O(lgn),如果使用一般方法插入操作

poj 2828 Buy Tickets 【线段树点更新】

题目:poj 2828 Buy Tickets 题意:有n个人排队,每个人有一个价值和要插的位置,然后当要插的位置上有人时所有的人向后移动一位当这个插入到这儿,如果没有直接插进去. 分析:分析发现直接插入移动的话花时间太多,我们可不可以用逆向思维.从后往前来,因为最后一个位置是肯定能确定的,而其他的则插入空的第某个位置. 比如第一组样例: 4 0 77 1 51 1 33 2 69 开始时候位置都为空 编号0 1 2 3 首先从最后一个来2 69 第二个位置空,则可以直接放 然后编号变为0 1

POJ 2828 Buy Tickets (线段树)

题目大意: 排队有人插队,每一次都插到第 i 个人的后面. 最后输出顺序. 思路分析: 你会发现,如果反向处理的话,你就知道这个人是第几个了. 那么问题一下子就简化了. 就是在线段树上找第几个空位置就行了. #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <vector> #define lson num<<1

poj 2828 Buy Tickets(树状数组 | 线段树)

题目链接:poj 2828 Buy Tickets 题目大意:给定N,表示有个人,给定每个人站入的位置,以及这个人的权值,现在按队列的顺序输出每个人的权值. 解题思路:第K大元素,很巧妙,将人入队的顺序倒过来看,就是纯第K大问题,然后用树状数组还是线段树就都可以做了. C++ 线段树 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn

POJ 2828 Buy Tickets (线段树,区间修改)

逆向思维.从最后一位开始考虑,用线段树查询空位置. #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <set> #include &l

POJ 2828 Buy Tickets (线段树 单点更新 变形)

题目链接 题意:有N个人排队,给出各个人想插队的位置和标识,要求输出最后的序列. 分析:因为之前的序列会因为插队而变化,如果直接算时间复杂度很高,所以可以用 线段树逆序插入,把序列都插到最后一层,len记录该区间里还剩余多少位置,插入的时候就插到剩余的第几个位置, 比如1,2已经插入了,如果再想插入第3个位置,则实际上插入的是5. 因为是逆序的,所以在3之前除了现在的1,2 还有之前的1,2. 1 #include <iostream> 2 #include <cstdio> 3

POJ 2828 Buy Tickets(排队问题,线段树应用)

POJ 2828 Buy Tickets(排队问题,线段树应用) ACM 题目地址:POJ 2828 Buy Tickets 题意: 排队买票时候插队. 给出一些数对,分别代表某个人的想要插入的位置Pos_i和他的Val_i,求出最后的队列的val顺序. 分析: 也是一道非常巧妙的题目. 刚開始天真的以为sort一下即可了.wa了一发后发现我错了... 原来能够非常巧妙的用线段树做.因为某个人想要插入posi位置,插入后他就在posi位置上了,可是可能其它人会插到他前面来,他的位置就会变成[在他