POJ 2828 Buy Tickets (线段树 or 树状数组+二分)

题目链接:http://poj.org/problem?id=2828

题意就是给你n个人,然后每个人按顺序插队,问你最终的顺序是怎么样的。

反过来做就很容易了,从最后一个人开始推,最后一个人位置很容易就确定了,那最后第二个人的位置也可以推(与最后一个人的位置无关)...依次就都可以确定所有的人了。

用前缀和的思想,要是这个人的位置确定了,那么就标记这个人位置的值为0,然后回溯更新,跟求逆序对个数的思想比较类似。

线段树:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 const int MAXN = 2e5 + 5;
 6 struct segtree {
 7     int l , r , val;
 8 }T[MAXN << 2];
 9 int x[MAXN] , y[MAXN] , ans[MAXN] , id;
10
11 void init(int p , int l , int r) {
12     T[p].l = l , T[p].r = r;
13     int mid = (l + r) >> 1;
14     if(l == r) {
15         T[p].val = 1;
16         return ;
17     }
18     init(p << 1 , l , mid);
19     init((p << 1)|1 , mid + 1 , r);
20     T[p].val = T[p << 1].val + T[(p << 1)|1].val;
21 }
22
23 void updata(int p , int num) {
24     int mid = (T[p].l + T[p].r) >> 1;
25     if(T[p].l == T[p].r) {
26         T[p].val = 0;
27         id = T[p].l;
28         return ;
29     }
30     if(num <= T[p << 1].val) {
31         updata(p << 1 , num);
32     }
33     else {
34         updata((p << 1)|1 ,  num - T[p << 1].val);
35     }
36     T[p].val = T[p << 1].val + T[(p << 1)|1].val;
37 }
38
39 int main()
40 {
41     int n;
42     while(~scanf("%d" , &n)) {
43         init(1 , 1 , n);
44         for(int i = 0 ; i < n ; i++) {
45             scanf("%d %d" , x + i , y + i);
46             x[i]++;
47         }
48         for(int i = n - 1 ; i >= 0 ; i--) {
49             updata(1 , x[i]);
50             ans[id] = y[i];
51         }
52         for(int i = 1 ; i < n ; i++) {
53             printf("%d " , ans[i]);
54         }
55         printf("%d\n" , ans[n]);
56     }
57 }

树状数组+二分:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 const int MAXN = 2e5 + 5;
 6 int bit[MAXN] , n , x[MAXN] , y[MAXN] , ans[MAXN];
 7
 8 inline void add(int i , int num) {
 9     for( ; i <= n ; i += (i & -i)) {
10         bit[i] += num;
11     }
12 }
13
14 int sum(int i) {
15     int s = 0;
16     for( ; i > 0 ; i -= (i & -i))
17         s += bit[i];
18     return s;
19 }
20
21 int main()
22 {
23     while(~scanf("%d" , &n)) {
24         memset(bit , 0 , sizeof(bit));
25         for(int i = 1 ; i <= n ; i++) {
26             scanf("%d %d" , x + i , y + i);
27             x[i]++;
28             add(i , 1);
29         }
30         for(int i = n ; i >= 1 ; i--) {
31             int l = 1 , r = n , mid;
32             while(l < r) {
33                 mid = (l + r) >> 1;
34                 if(sum(mid) >= x[i])
35                     r = mid;
36                 else
37                     l = mid + 1;
38             }
39             ans[l] = y[i];
40             add(l , -1);
41         }
42         for(int i = 1 ; i < n ; i++) {
43             printf("%d " , ans[i]);
44         }
45         printf("%d\n" , ans[n]);
46     }
47 }
时间: 2024-12-29 23:54:39

POJ 2828 Buy Tickets (线段树 or 树状数组+二分)的相关文章

POJ 2828 Buy Tickets (线段树)

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

POJ 2828 Buy Tickets 线段树解法

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

POJ 2828 Buy Tickets(线段树单点)

https://vjudge.net/problem/POJ-2828 题目意思:有n个数,进行n次操作,每次操作有两个数pos, ans.pos的意思是把ans放到第pos 位置的后面,pos后面的数就往后推一位.最后输出每个位置的ans. 思路:根据题 目可知,最后插入的位置的数才是最终不变的数,所以可以从最后的输入作第1个放入,依此类推,倒插入.在插入时也有一定的技术,首先创建一棵空线段树时,每个节点记录当前范围内有多少个空位置.在插入时,要注意,一个数放入之后那么这个位置就不用管了,那么

POJ 2828 Buy Tickets(线段树&#183;插队)

题意  n个人排队  每个人都有个属性值  依次输入n个pos[i]  val[i]  表示第i个人直接插到当前第pos[i]个人后面  他的属性值为val[i]  要求最后依次输出队中各个人的属性值 从头到尾看的话  队列是动态的   无法操作  但是反过来看时  pos[i]就可以表示第i个人前面还有多少个人了  然后想到了用线段树做就简单了  线段树维护对应区间还有多少个空位  每次把i放到前面刚好有pos[i]个空位的位置就行了  具体看代码 #include <cstdio> #de

POJ 2828 Buy Tickets | 线段树的喵用

题意: 给你n次插队操作,每次两个数,pos,w,意为在pos后插入一个权值为w的数; 最后输出1~n的权值 题解: 首先可以发现,最后一次插入的位置是准确的位置 所以这个就变成了若干个子问题, 所以用线段树维护一下每个区间剩余多少位置可选 对于一个pos 如果左儿子的剩余超过当前位置,就递归进左子树 反之就相当于留出了左儿子剩余的位置,递归进右子树,当前位置变成pos-左儿子剩余位置 请注意是在后面插入 1 #include<cstdio> 2 #include<algorithm&g

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(神题!线段树or树状数组)

题目链接:POJ 2828 Buy Tickets [题意]给了你 n(1<=n<=200000)个人的插队信息,让你输出最终的队列的排列 [思路]常规思考的话,这道题就是模拟,但是时间复杂度一定会很高.POJ的discuss上说这道题是神题,难得不是用什么数据结构,而是思路,这道题要逆向去思考,从最后一个人往前看,后插进来的人更容易定位,他一定能站到他想的位置,并且不会在挪动.再前一个人呢?他的位置即是接下来的空位的编号.于是有线段树用于维护位置信息.当然用树状数组也是可以做的,但是要加上二

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