P2824 [HEOI2016/TJOI2016] 排序
这题是我在做数据结构时做到的,看了题解,发现这种思想挺重要的,记录一下
题目意思很明确,对于一个长度为n排列A进行m次操作,每次操作对于区间[l,r]进行升序或降序排序,询问在m次操作后位置q的数是多少。
考虑二分答案,将大于等于mid的数记为1,小于mid的数记为0,每次排序就是将一个区间划分为两部分,一部分全为1,另一部分全为0。使用线段树维护即可。
下面是我打的代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
int n, m, q, a[N];
struct tt {
int op, l, r;
}b[N];
struct SEG {
int sum[4 * N], col[4 * N], tag[4 * N];
void push_up(int rt, int ls, int rs) {
if (col[ls] != col[rs] || col[ls] == -1 || col[rs] == -1) col[rt] = -1;
else col[rt] = col[ls];
sum[rt] = sum[ls] + sum[rs];
}
void push_down(int rt, int l, int r, int ls, int rs) {
if (tag[rt] == -1) return ;
int mid = (l + r) / 2;
col[ls] = tag[rt];
sum[ls] = tag[rt] * (mid - l + 1);
tag[ls] = tag[rt];
col[rs] = tag[rt];
sum[rs] = tag[rt] * (r - mid);
tag[rs] = tag[rt];
tag[rt] = -1;
}
void update(int root, int l, int r, int pl, int pr, int val) {
if (pl > pr) return ;
if (pl <= l && pr >= r) {
col[root] = val;
sum[root] = (r - l + 1) * val;
tag[root] = val;
return ;
}
push_down(root, l, r, root * 2, root * 2 + 1);
int mid = (l + r) / 2;
if (pl <= mid) update(root * 2, l, mid, pl, pr, val);
if (pr > mid) update(root * 2 + 1, mid + 1, r, pl, pr, val);
push_up(root, root * 2, root * 2 + 1);
}
int query(int root, int l, int r, int pl, int pr) {
if (pl <= l && pr >= r) return sum[root];
push_down(root, l, r, root * 2, root * 2 + 1);
int mid = (l + r) / 2, res = 0;
if (pl <= mid) res += query(root * 2, l, mid, pl, pr);
if (pr > mid) res += query(root * 2 + 1, mid + 1, r, pl, pr);
return res;
}
}seg;
bool check(int mid) {
seg.update(1, 1, n, 1, n, 0);
for (int i = 1; i <= n; i++) {
if (a[i] >= mid) seg.update(1, 1, n, i, i, 1);
}
for (int i = 1; i <= m; i++) {
if (b[i].op == 0) {
int cnt = seg.query(1, 1, n, b[i].l, b[i].r);
seg.update(1, 1, n, b[i].l, b[i].r - cnt, 0);
seg.update(1, 1, n, b[i].r - cnt + 1, b[i].r, 1);
}else {
int cnt = seg.query(1, 1, n, b[i].l, b[i].r);
seg.update(1, 1, n, b[i].l, b[i].l + cnt - 1, 1);
seg.update(1, 1, n, b[i].l + cnt, b[i].r, 0);
}
}
return seg.query(1, 1, n, q, q) == 1;
}
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 1; i <= m; i++) scanf("%lld%lld%lld", &b[i].op, &b[i].l, &b[i].r);
cin >> q;
int l = 1, r = n, ans;
memset(seg.tag, -1, sizeof(seg.tag));
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
l = mid + 1;
}else r = mid - 1;
}
cout << ans;
}
对于这种思想可以解决诸多问题,将问题和操作简化来实现目的。不用关心每个数具体是多少,而是关心相对大小,将数字进行分组。还有类似的题需要求平均数的也用到了这种思想,二分答案,将每个数减去这个答案,看看有没有区间和大于等于0。
浙公网安备 33010602011771号