P2824 [HEOI2016/TJOI2016] 排序
首先,序列若干次操作一定有局部的子区间是有序的,但是直接维护区间内每个数的顺序的话,就会超时。所以我们可以用权值线段树来维护(维护的是每个数落在那个值上,区间个数)。
对于排序操作,操作区间一定为若干个有序区间全部或一部分(显然这种情况只能在区间端点处),我们可以用线段树合并将这些部分合并。对于有序区间的一部分,可以用线段树分裂。
那么问题来了,如何找到那些操作区间覆盖(或部分覆盖)到的区间呢?
可以用 set 维护若干个段 \([l,r]\),二分查找即可,剩下的操作就类似珂朵莉树了。
第 \(q\) 位置上的和就找到对应的线段树,线段树上二分即可。
代码写完再贴
写完了,调了半天。
#include <bits/stdc++.h>
#define ls son[p][0]
#define rs son[p][1]
using namespace std;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = c == '-' ? -1 : f, c = getchar();
while (c >= '0' && c <= '9') x = (x<<3)+(x<<1)+(c^48), c = getchar();
return x*f;
}
inline void write(int x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x/10);
putchar('0'+x%10);
}
typedef set <int> :: iterator It;
const int N = 1e5+5, M = 6e6;
int n, m, q, cnt, tot, rt[N], op[M], gb[M], son[M][2], val[M];
set <int> s;
inline int newnode() { return tot ? gb[tot--] : ++cnt; }
inline void del(int p) { gb[++tot] = p; ls = rs = val[p] = 0; }
inline void modify(int &p, int l, int r, int x) {
if (!p) p = newnode();
val[p] = 1;
if (l == r) return;
int mid = l+r>>1;
if (x <= mid) modify(ls, l, mid, x);
else modify(rs, mid+1, r, x);
}
inline void merge(int &x, int y) {
if (!x || !y) return (void)(x |= y);
val[x] += val[y];
merge(son[x][0], son[y][0]);
merge(son[x][1], son[y][1]);
del(y);
}
inline void split(int &x, int y, int k, int tag) {
if (!y) return;
x = newnode();
int v = val[son[y][tag]];
if (k > v) split(son[x][tag^1], son[y][tag^1], k-v, tag);
else swap(son[x][tag^1], son[y][tag^1]);
if (k < v) split(son[x][tag], son[y][tag], k, tag);
val[x] = val[y]-k, val[y] = k;
}
inline int solve(int p, int l, int r) {
if (l == r) return l;
int mid = l+r>>1;
return val[ls] ? solve(ls, l, mid) : solve(rs, mid+1, r);
}
inline It split_(int p) {
It it = s.lower_bound(p);
if (*it == p) return it;
--it; split(rt[p], rt[*it], p-*it, op[p] = op[*it]);
return s.insert(p).first;
}
inline void assign(int l, int r, int tag) {
It end = split_(r+1), begin = split_(l);
for (It it = ++begin; it != end; ++it) merge(rt[l], rt[*it]);
s.erase(begin, end);
op[l] = tag;
}
int main() {
n = read(), m = read();
s.insert(n+1);
for (int i = 1; i <= n; ++i) s.insert(i), modify(rt[i], 1, n, read());
while (m--) {
int op = read(), l = read(), r = read();
assign(l, r, op);
}
q = read();
split_(q); split_(q+1);
write(solve(rt[q], 1, n));
return 0;
}
这题还有离线二分的解法?写了一下,解法看看题解吧。
#include <bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
using namespace std;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = c == '-' ? -1 : f, c = getchar();
while (c >= '0' && c <= '9') x = (x<<3)+(x<<1)+(c^48), c = getchar();
return x*f;
}
inline void write(int x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x/10);
putchar('0'+x%10);
}
const int N = 1e5+5, M = N<<2;
int n, m, q, mid_, a[N], op[N], L[N], R[N], tag[M], sum[M];
inline void pushup(int p) { sum[p] = sum[ls]+sum[rs]; }
inline void pushdown(int p, int len) {
if (tag[p] == -1) return;
tag[ls] = tag[rs] = tag[p];
if (tag[p]) sum[ls] = len-(len>>1), sum[rs] = len>>1;
else sum[ls] = sum[rs] = 0;
tag[p] = -1;
}
inline void build(int p, int l, int r) {
tag[p] = -1;
if (l == r) {
sum[p] = a[l] >= mid_;
return;
}
int mid = l+r>>1;
build(ls, l, mid);
build(rs, mid+1, r);
pushup(p);
}
inline void modify(int p, int l, int r, int ql, int qr, int x) {
if (qr < ql) return;
if (ql <= l && r <= qr) {
sum[p] = x*(r-l+1), tag[p] = x;
return;
}
pushdown(p, r-l+1);
int mid = l+r>>1;
if (ql <= mid) modify(ls, l, mid, ql, qr, x);
if (mid < qr) modify(rs, mid+1, r, ql, qr, x);
pushup(p);
}
inline int query(int p, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return sum[p];
pushdown(p, r-l+1);
int mid = l+r>>1, sum = 0;
if (ql <= mid) sum += query(ls, l, mid, ql, qr);
if (mid < qr) sum += query(rs, mid+1, r, ql, qr);
return sum;
}
inline int solve(int p, int l, int r, int x) {
if (l == r) return sum[p];
pushdown(p, r-l+1);
int mid = l+r>>1;
return x <= mid ? solve(ls, l, mid, x) : solve(rs, mid+1, r, x);
}
bool check() {
build(1, 1, n);
for (int i = 1; i <= m; ++i) {
int cnt = query(1, 1, n, L[i], R[i]);
if (op[i]) {
modify(1, 1, n, L[i], L[i]+cnt-1, 1);
modify(1, 1, n, L[i]+cnt, R[i], 0);
}
else {
modify(1, 1, n, R[i]-cnt+1, R[i], 1);
modify(1, 1, n, L[i], R[i]-cnt, 0);
}
}
return solve(1, 1, n, q);
}
int main() {
n = read(), m = read();
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1; i <= m; ++i) op[i] = read(), L[i] = read(), R[i] = read();
q = read();
int l = 1, r = n;
while (l < r) {
mid_ = l+r+1>>1;
if (check()) l = mid_;
else r = mid_-1;
}
write(l);
return 0;
}