洛谷 P2824 [HEOI2016/TJOI2016]排序
Description
Solution
一眼就能想到线段树,但是线段树似乎也无法维护啊?那怎么做?
于是我们查看标签……
发现……二分答案?这怎么二分答案??
于是我们再回想一下题目要求计算什么:位置是 \(q\) 的数是多少。
而且只有一次询问。
既然是二分答案,那我们就二分呗。
假设答案是 \(mid\),那现在的问题就是判断 \(mid\) 是不是操作完之后是第 \(q\) 个数。
考虑到线段树只能进行区间维护,比如区间加,区间修改,区间求和什么的……
诶,等等,如果我们把小于 \(mid\) 的数都重新赋值为 0,大于等于 \(mid\) 的数都赋值为 1,那这个排序是不是就相当于区间赋值了呢?对于从小到大排序,我们不需要知道数到底是多少,只需要把 0 全都放到区间的前半段,1 放到后半段即可。反之同理。
操作完之后,查询第 \(q\) 位是 0 还是 1,如果是 0,说明这个 \(mid\) 太大了,需要调小,反之则需要调大。
这样一来,这道题就可以完美的解决了,时间复杂度 \(O(nlog^2_n)\)。
最后再讲一下如何区间修改:我们记录一下区间和,那么这个区间和就是区间里 1 的个数,每次修改时先查询一下区间和,然后覆盖即可,具体看代码吧。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
inline int read(){
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
const int N = 1e5 + 10;
int n, m, maxs, p, ans;
int a[N], sum[N << 2], lazy[N << 2];
bool b[N];
struct node{
int op, l, r;
}q[N];
inline void pushup(int rt){
sum[rt] = sum[ls] + sum[rs];
}
inline void pushdown(int l, int r, int rt){
if(lazy[rt] != -1){
int mid = (l + r) >> 1;
sum[ls] = lazy[rt] * (mid - l + 1);
sum[rs] = lazy[rt] * (r - mid);
lazy[ls] = lazy[rs] = lazy[rt];
lazy[rt] = -1;
}
}
inline void build(int l, int r, int rt){
lazy[rt] = -1;
if(l == r){
sum[rt] = b[l];
return;
}
int mid = (l + r) >> 1;
build(l, mid, ls);
build(mid + 1, r, rs);
pushup(rt);
}
inline int query(int L, int R, int l, int r, int rt){
if(L <= l && r <= R) return sum[rt];
pushdown(l, r, rt);
int mid = (l + r) >> 1;
int res = 0;
if(L <= mid) res += query(L, R, l, mid, ls);
if(R > mid) res += query(L, R, mid + 1, r, rs);
pushup(rt);
return res;
}
inline void update(int L, int R, int k, int l, int r, int rt){
if(L <= l && r <= R){
sum[rt] = k * (r - l + 1);
lazy[rt] = k;
return;
}
pushdown(l, r, rt);
int mid = (l + r) >> 1;
if(L <= mid) update(L, R, k, l, mid, ls);
if(R > mid) update(L, R, k, mid + 1, r, rs);
pushup(rt);
}
inline bool check(int mid){
for(int i = 1; i <= n; i++)
b[i] = a[i] >= mid;
build(1, n, 1);
for(int i = 1; i <= m; i++){
int s = query(q[i].l, q[i].r, 1, n, 1);
if(!s || s == q[i].r - q[i].l + 1) continue;
if(!q[i].op) update(q[i].l, q[i].r - s, 0, 1, n, 1), update(q[i].r - s + 1, q[i].r, 1, 1, n, 1);
else update(q[i].l, q[i].l + s - 1, 1, 1, n, 1), update(q[i].l + s, q[i].r, 0, 1, n, 1);
}
return query(p, p, 1, n, 1);
}
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++)
a[i] = read();
for(int i = 1; i <= m; i++)
q[i].op = read(), q[i].l = read(), q[i].r = read();
p = read();
int l = 1, r = n;
while(l <= r){
int mid = (l + r) >> 1;
if(check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf("%d\n", ans);
return 0;
}