BZOJ 4552 排序(二分 || 线段树合并)
来源:互联网 发布:阿里云邮箱app 编辑:程序博客网 时间:2024/06/15 06:51
题意:对一个1~n的全排列, 进行m次局部排序
(0, l, r)将区间[l, r]数字升序排列
(1, l, r)将区间[l, r]数字降序排列
(0, l, r)将区间[l, r]数字升序排列
(1, l, r)将区间[l, r]数字降序排列
仅一次询问, 询问m次局部排序后第q位置上的数字
1 ≤ n, m, q ≤ 1e5
思路:
这题有个神奇的做法,我们可以直接二分答案x,然后把大于等于x的都看作1,比x小的都看作0插入线段树中,
然后执行m个询问,因为数组中要么是0,要么是1,所以区间排序就简单多了,只要求出区间1的个数,然后把这些1如
果升序放到后面,如果降序放到前面。执行完操作后看下q位置是0还是1即可,如果是1就说明比x大,反之比x小。
时间复杂度O(m log^2(n))
当然还有个麻烦点的做法,但是可以支持在线多次询问。(还没实现)
初始时建立n棵只包含一个点的线段树 (动态开点, 范围均为1~n)
• 用线段树的合并完成排序
• 对于每次局部 ([a, b]) 排序
• 从若干线段树 (将每棵线段树代表的区间插入set, 方便每次局部排序快速的找出第一棵包含[a, b]的线段树)中分裂出在[a, b]中的点, 相当于分裂出线段树中连续的一段
• 将分裂出的若干子线段树合并• 查询相当于查询某个线段树中的第k个值
• 时间复杂度O(n log n)
• 并且支持在线多个询问及局部排序
代码:
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn = 1e5+5;const int INF = 0x3f3f3f3f;int tree[maxn*4], set[maxn*4], a[maxn];int cmd[maxn], L[maxn], R[maxn];int n, m, p;void pushup(int root){ tree[root] = tree[root*2]+tree[root*2+1];}void pushdown(int root, int l, int r){ int mid = (l+r)/2; if(set[root] != -1) { set[root*2] = set[root*2+1] = set[root]; tree[root*2] = set[root]*(mid-l+1); tree[root*2+1] = set[root]*(r-mid); set[root] = -1; }}void update(int root, int l, int r, int pos, int val){ if(l == r) { tree[root] = val; return ; } int mid = (l+r)/2; if(pos <= mid) update(root*2, l, mid, pos, val); else update(root*2+1, mid+1, r, pos, val); pushup(root);}void Set(int root, int l, int r, int i, int j, int val){ if(i <= l && j >= r) { set[root] = val; tree[root] = val*(r-l+1); return ; } pushdown(root, l, r); int mid = (l+r)/2; if(i <= mid) Set(root*2, l, mid, i, j, val); if(j > mid) Set(root*2+1, mid+1, r, i, j, val); pushup(root);}int query(int root, int l, int r, int i, int j){ if(i <= l && j >= r) return tree[root]; pushdown(root, l, r); int sum = 0; int mid = (l+r)/2; if(i <= mid) sum += query(root*2, l, mid, i, j); if(j > mid) sum += query(root*2+1, mid+1, r, i, j); return sum;}bool judge(int x){ memset(set, -1, sizeof(set)); for(int i = 1; i <= n; i++) { if(a[i] >= x) update(1, 1, n, i, 1); else update(1, 1, n, i, 0); } for(int i = 1; i <= m; i++) { int num = R[i]-L[i]+1; int big = query(1, 1, n, L[i], R[i]); if(cmd[i]) { if(L[i]+big-1 >= L[i]) Set(1, 1, n, L[i], L[i]+big-1, 1); if(L[i]+big <= R[i]) Set(1, 1, n, L[i]+big, R[i], 0); } else { if(R[i]-big+1 <= R[i]) Set(1, 1, n, R[i]-big+1, R[i], 1); if(R[i]-big >= L[i]) Set(1, 1, n, L[i], R[i]-big, 0); } } return query(1, 1, n, p, p);}int main(void){ while(cin >> n >> m) { int l = INF, r = -INF; for(int i = 1; i <= n; i++) scanf("%d", &a[i]), l = min(l, a[i]), r = max(r, a[i]); for(int i = 1; i <= m; i++) scanf("%d%d%d", &cmd[i], &L[i], &R[i]); scanf("%d", &p); int ans = a[1]; while(l <= r) { int mid = (l+r)/2; if(judge(mid)) ans = mid, l = mid+1; else r = mid-1; } printf("%d\n", ans); } return 0;}
阅读全文
1 0
- BZOJ 4552 排序(二分 || 线段树合并)
- [BZOJ]4552 [TJOI2016] 排序 二分 + 线段树
- [BZOJ]2653: middle 线段树合并+二分
- bzoj 4552: [Tjoi2016&Heoi2016]排序 二分答案+线段树
- bzoj 4552 排序 线段树+二分 解题报告
- BZOJ 4552 [Tjoi2016&Heoi2016]排序 线段树+二分
- BZOJ 4552: [Tjoi2016&Heoi2016]排序 二分 线段树
- 【BZOJ】4552 [Tjoi2016&Heoi2016]排序 二分+线段树
- BZOJ 4552: [Tjoi2016&Heoi2016]排序 [二分][线段树]
- [Treap套权值线段树 线段树分裂与合并] BZOJ 4552 [Tjoi2016&Heoi2016]排序
- bzoj 4756(线段树合并)
- bzoj 2212(线段树合并)
- BZOJ 3123 线段树合并
- [BZOJ]2809: [Apio2012]dispatching 主席树(线段树合并)
- bzoj 2733: [HNOI2012]永无乡(线段树合并)
- bzoj 4756: [Usaco2017 Jan]Promotion Counting (线段树合并)
- bzoj 3307: 雨天的尾巴 (线段树合并+LCA)
- BZOJ 2212 & POI 18 Tree Rotations(线段树合并)
- HDU-6096 String(字典树+线段树扫描线)
- Matplotlib折线图
- 关于MongoDB中,find()和findOne()的区别
- 使用soci操作数据库
- Mysql索引的优化分析-索引的简介
- BZOJ 4552 排序(二分 || 线段树合并)
- 关闭流
- HDU 6105 Gameia
- jsoup入门
- 字符串操作函数
- 日期范围选择类日历(增强版)
- cookie 和session 的区别详解
- Apply call
- JAVA 标识符的常见的命名规则