[二分] [线段树] P2824 [HEOI2016_TJOI2016] 排序
posted on 2024-06-19 05:14:46 | under | source
离线做法:
考虑到对序列直接排序是 hard 的,但是若 \(a\in \{0,1\}\) 的话不难用线段树直接维护。具体来说,算出区间 \(1\) 的个数,升序排列就是将所有 \(0\) 放在左边,\(1\) 放在右边,降序同理。
怎么转化成这种形式?可以使用二分,令 \(c_i=[a_i\ge mid]\),对 \(01\) 序列 \(c\) 进行操作即可。若最后 \(c_q=1\),则也有 \(a_q\ge mid\)。
好像有点玄乎,但是仔细想想,因为是二分答案,所以只需要判断 \(a\) 与 \(mid\) 的偏序关系,若 \(c_x=c_y\) 那么就不需要考虑它们的相对顺序——因为 \(a_x,a_y\) 在此时是“等价”的。于是直接对 \(c\) 序列处理即可。
在线做法:
使用线段树合并、分裂。
将序列视作若干个有序区间,可以用 set 维护,然后对它们每个都单独开一颗权值线段树。
那么对于被操作区间完全覆盖的有序区间,对它们做线段树合并即可。
而于其余的区间(至多两个),对它们做线段树分裂得到被包含的部分,然后它们再合并进大区间即可。
代码
离线。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n, m, a[N], q, lim;
struct modify{int l, r, opt;} b[N];
namespace Sg_Tree{
#define lt (u << 1)
#define rt (u << 1 | 1)
#define mid (l + r >> 1)
int t[N << 2], tag[N << 2];
inline void psup(int u) {t[u] = t[lt] + t[rt];}
inline void psdw(int u, int l, int r){
if(tag[u] != -1) t[lt] = tag[u] * (mid - l + 1), t[rt] = tag[u] * (r - mid), tag[lt] = tag[rt] = tag[u];
tag[u] = -1;
}
inline void build(int u, int l, int r){
tag[u] = -1;
if(l == r) {t[u] = {a[l] >= lim}; return ;}
build(lt, l, mid), build(rt, mid + 1, r);
psup(u);
}
inline void upd(int u, int l, int r, int ll, int rr, int p){
if(ll > rr) return ;
if(ll <= l && r <= rr) {t[u] = p * (r - l + 1), tag[u] = p; return ;}
psdw(u, l, r);
if(ll <= mid) upd(lt, l, mid, ll, rr, p);
if(rr > mid) upd(rt, mid + 1, r, ll, rr, p);
psup(u);
}
inline int fid(int u, int l, int r, int ll, int rr){
if(ll > rr) return 0;
if(ll <= l && r <= rr) return t[u];
psdw(u, l, r); int res = 0;
if(ll <= mid) res += fid(lt, l, mid, ll, rr);
if(rr > mid) res += fid(rt, mid + 1, r, ll, rr);
psup(u); return res;
}
}using namespace Sg_Tree;
inline bool check(int Mid){
lim = Mid;
build(1, 1, n);
for(int i = 1; i <= m; ++i){
int cnt = fid(1, 1, n, b[i].l, b[i].r);
if(!b[i].opt)
upd(1, 1, n, b[i].l, b[i].r - cnt, 0), upd(1, 1, n, b[i].r - cnt + 1, b[i].r, 1);
else
upd(1, 1, n, b[i].l, b[i].l + cnt - 1, 1), upd(1, 1, n, b[i].l + cnt, b[i].r, 0);
}
return fid(1, 1, n, q, q);
}
signed main(){
cin >> n >> m;
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for(int i = 1; i <= m; ++i) scanf("%d%d%d", &b[i].opt, &b[i].l, &b[i].r);
cin >> q;
int L = 1, R = n + 1, Mid;
while(L + 1 < R){
Mid = (L + R) >> 1;
if(check(Mid)) L = Mid;
else R = Mid;
}
cout << L;
return 0;
}

浙公网安备 33010602011771号