【BZOJ4552】【HEOI2016】排序 [二分答案][线段树]

排序

Time Limit: 60 Sec  Memory Limit: 256 MB
[Submit][Status][Discuss]

Description

  在2016年,佳媛姐姐喜欢上了数字序列。

  因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。

  这个难题是这样子的:

  给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:

    1: (0,l,r)表示将区间[l,r]的数字升序排序

    2: (1,l,r)表示将区间[l,r]的数字降序排序

  最后询问第q位置上的数字。

Input

  输入数据的第一行为两个整数n和m。

  n表示序列的长度,m表示局部排序的次数。

  第二行为n个整数,表示1到n的一个全排列。

  接下来输入m行,每一行有三个整数op, l, r,

   op为0代表升序排序,op为1代表降序排序, l, r 表示排序的区间。

  最后输入一个整数q,q表示排序完之后询问的位置。

Output

  输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。

Sample Input

  6 3
  1 6 2 5 3 4
  0 1 4
  1 3 6
  0 2 4
  3

Sample Output

  5

HINT

  1 <= n <= 10^5,1 <= m <= 10^5, 1 <= q <= n。

Solution

  我们先考虑如果权值很小的话怎么做,显然可以对每个权值开一个线段树维护在哪些位置出现过。

  那么排序显然就是覆盖连续的一段。只要知道某一区间有几个这个权值即可。

  但是这样显然是过不了的,于是我们考虑二分答案,把val >= mid的设为1,其余的设为0

  这样就把权值变成了0/1,那么显然我们按照以上操作,如果Q位置上是1说明mid<=Ans还可以更大一点否则说明mid>Ans

  只要支持区间求和以及区间覆盖0/1即可。

Code

  1 #include<iostream>
  2 #include<string>
  3 #include<algorithm>
  4 #include<cstdio>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cmath>
  8 using namespace std;
  9 typedef long long s64;
 10
 11 const int ONE = 400005;
 12 const int MOD = 1e9 + 7;
 13
 14 int get()
 15 {
 16         int res = 1, Q = 1; char c;
 17         while( (c = getchar()) < 48 || c > 57)
 18             if(c == ‘-‘) Q = -1;
 19         if(Q) res = c - 48;
 20         while( (c = getchar()) >= 48 && c <= 57)
 21             res = res * 10 + c - 48;
 22         return res * Q;
 23 }
 24
 25 int n, m, Q;
 26 int a[ONE];
 27 int res, now;
 28
 29 struct power
 30 {
 31         struct point
 32         {
 33             int val, tag;
 34         }Node[ONE];
 35
 36         void Build(int i, int l, int r)
 37         {
 38             Node[i].tag = -1;
 39             if(l == r) return;
 40             int mid = l + r >> 1;
 41             Build(i << 1, l, mid);
 42             Build(i << 1 | 1, mid + 1, r);
 43         }
 44
 45         int pushdown(int i, int l, int r)
 46         {
 47             int mid = l + r >> 1;
 48             if(Node[i].tag != -1)
 49             {
 50                 Node[i << 1].tag = Node[i].tag;
 51                 Node[i << 1].val = Node[i].tag * (mid - l + 1);
 52                 Node[i << 1 | 1].tag = Node[i].tag;
 53                 Node[i << 1 | 1].val = Node[i].tag * (r - (mid + 1) + 1);
 54                 Node[i].tag = -1;
 55             }
 56         }
 57
 58         void Update(int i, int l, int r, int L, int R, int x)
 59         {
 60             if(L > R) return;
 61             if(L <= l && r <= R)
 62             {
 63                 Node[i].tag = x;
 64                 Node[i].val = x * (r - l + 1);
 65                 return;
 66             }
 67             pushdown(i, l, r);
 68             int mid = l + r >> 1;
 69             if(L <= mid) Update(i << 1, l, mid, L, R, x);
 70             if(mid + 1 <= R) Update(i << 1 | 1, mid + 1, r, L, R, x);
 71             Node[i].val = Node[i << 1].val + Node[i << 1 | 1].val;
 72         }
 73
 74         void Query(int i, int l, int r, int L, int R)
 75         {
 76             if(L > R) return;
 77             if(L <= l && r <= R)
 78             {
 79                 res += Node[i].val;
 80                 return;
 81             }
 82             pushdown(i, l, r);
 83             int mid = l + r >> 1;
 84             if(L <= mid) Query(i << 1, l, mid, L, R);
 85             if(mid + 1 <= R) Query(i << 1 | 1, mid + 1, r, L, R);
 86         }
 87 }C[2];
 88
 89 struct operate
 90 {
 91         int l, r, x;
 92 }oper[ONE];
 93
 94 void Modify(int id, int Left, int Right)
 95 {
 96         res = 0;
 97         C[id].Query(1, 1, n, Left, Right);
 98         C[id].Update(1, 1, n, Left, Right, 0);
 99         C[id].Update(1, 1, n, now, now + res - 1, 1);
100         now += res;
101 }
102
103 int Check(int mid)
104 {
105         for(int i = 0; i <= 1; i++)
106             C[i].Node[1].tag = 0;
107
108         for(int i = 1; i <= n; i++)
109             C[a[i] >= mid].Update(1, 1, n, i, i, 1);
110
111         for(int i = 1; i <= m; i++)
112         {
113             now = oper[i].l;
114             if(oper[i].x == 0) for(int id = 0; id <= 1; id++) Modify(id, oper[i].l, oper[i].r);
115             if(oper[i].x == 1) for(int id = 1; id >= 0; id--) Modify(id, oper[i].l, oper[i].r);
116         }
117
118         res = 0, C[1].Query(1, 1, n, Q, Q);
119         return res;
120 }
121
122 int main()
123 {
124         n = get();  m = get();
125         for(int i = 1; i <= n; i++)
126             a[i] = get();
127         for(int i = 1; i <= m; i++)
128             oper[i].x = get(), oper[i].l = get(), oper[i].r = get();
129
130         Q = get();
131         int l = 1, r = n;
132         while(l < r - 1)
133         {
134             int mid = l + r >> 1;
135             if(Check(mid)) l = mid;
136             else r = mid;
137         }
138
139         if(Check(r)) printf("%d", r);
140         else printf("%d", l);
141 }
142 

  

时间: 2024-11-08 21:30:53

【BZOJ4552】【HEOI2016】排序 [二分答案][线段树]的相关文章

HDU 5649 DZY Loves Sorting(二分答案+线段树、线段树合并+线段树分割)

题意 一个 \(1\) 到 \(n\) 的全排列,\(m\) 种操作,每次将一段区间 \([l,r]\) 按升序或降序排列,求 \(m\) 次操作后的第 \(k\) 位. \(1 \leq n \leq 10^5\) 思路 两个 \(\log\) 的做法展现了二分答案的强大功能.首先二分枚举第 \(k\) 位的值,然后将小于等于它的数都变为 \(1\) ,大于它的数变为 \(0\) ,线段树可以实现对 \(01\) 序列快速的排序,按要求进行排序,然后如果第 \(k\) 位为 \(1\) 说明这

bzoj 4552 [Tjoi2016&amp;Heoi2016]排序——二分答案

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4552 二分答案,把 >= mid 的设成1.< mid 的设成0,之后排序就变成区间赋值了. #include<cstdio> #include<cstring> #include<algorithm> #define ls Ls[cr] #define rs Rs[cr] using namespace std; const int N=1e5+5,

2019杭电多校第四场hdu6621 K-th Closest Distance(二分答案+主席树)

K-th Closest Distance 题目传送门 解题思路 二分答案+主席树 先建主席树,然后二分答案mid,在l和r的区间内查询[p-mid, p+mid]的范围内的数的个数,如果大于k则说明这个范围内存在第k小的数,r=mid,否则不存在,l=mid+1. 代码如下 #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long ll; inline int read(){

[BZOJ] 4552: [Tjoi2016&amp;Heoi2016]排序 #二分+线段树+算法设计策略

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1451  Solved: 734[Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,r

[BZOJ4556][TJOI2016&amp;&amp;HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)

4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1360  Solved: 545[Submit][Status][Discuss] Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了 一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE O,嫁给高富帅,走上人生巅

【BZOJ3958】[WF2011]Mummy Madness 二分+扫描线+线段树

[BZOJ3958][WF2011]Mummy Madness Description 在2011年ACM-ICPC World Finals上的一次游览中,你碰到了一个埃及古墓. 不幸的是,你打开了坟墓之后,才发现这是一个坏主意:突然之间,原本空无一物的沙漠上已经爬满了暴躁的木乃伊.(如果你也沉睡几千年而突然被惊醒,你也会变得如此暴躁的.)(幸运的是,当你做完这道题的时候,你醒来了,发现你在弗罗里达的酒店里.那些木乃伊只是一场梦.) 面对这一大堆疯狂的木乃伊,你唯一的机会就是试图在他们抓到你之

51nod1287(二分、线段树)

链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1287 思路:二分查找大于等于b[i]的最前面的位置(想起一次错漏百出的网赛中一道没在比赛A掉的题),并更新线段树,总复杂度nlogn. 我并不是停留在舒适区,而是找回当年因为死套模板而没有去深刻理解的东西,下次再接触到这些东西的时候只能称呼你们学长学姐了. #include<bits/stdc++.h> using namespace std; typedef

Codeforces 460C 二分结果+线段树维护

发现最近碰到好多次二分结果的题目,上次多校也是,被我很机智的快速过了,这个思想确实非常不错.在正面求比较难处理的时候,二分结果再判断是否有效往往柳暗花明. 这个题目给定n个数字的序列,可以操作m次,每次要操作w个连续的数字,每次的操作将使得该段连续数字的数都+1,最后求整个序列最小值的最大值 求最小值最大,明显的二分结果的题目,我一开始还是在ACdream那个群里看到这个题,说是二分+线段树的题目,我就来做了一下..首先二分部分很容易,下界就是初始序列的最小值,上界就是 下界+m,至于怎么判断这

[比赛] Codeforces Round #538 (Div. 2) solution (贪心,数学其他,二分,线段树)

已经写了100篇题解啦! link solution pdf #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int read(){ int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c