$$BZOJ4552[HEOI2016/TJOI2016] 排序$$
- 呵呵本来是因为改不动校内模拟赛一道题然后想着做一道线段树分裂的板子题水一水算了,
结果板子题做了一天
- 自己垃圾无话可说
- 主要操作就是分裂前\(K\)大,具体的就是在分裂时改变\(size\)就好了,但有一些细节要注意;合并比较好处理,就是普通的线段树合并
- 对每个点开一颗动态开点的权值线段树,用一些权值线段树维护一一对应的有序区间,区间排序就是要将边缘的线段树分裂,然后中间的全部合到一起,还要用\(set\)维护一下这些线段树之间的关系;
- 直接按照上面的定义实现的,写得丑的一比,
在洛谷上还过不了,也许是它的数据有问题,好像别人的线段树分裂也是\(40\)分,不过\(BZOJ\)和\(LOJ\)都过了.
#include <bits/stdc++.h>
typedef long long LL;
typedef std::pair<int, int> PII;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define st first
#define ed second
std::string procStatus()
{
std::ifstream buf("/proc/self/status");
return std::string(std::istreambuf_iterator<char>(buf), std::istreambuf_iterator<char>());
}
inline int read(int Num = 0, int Flag = 1)
{
char c = getchar();
for (; !isdigit(c); c = getchar())
if (c == '-')
Flag = -1;
for (; isdigit(c); c = getchar())
Num = Num*10 + c-'0';
return Num *= Flag;
}
template <typename T> bool chkmax(T &a, T b) { return a < b? a = b, true : false; }
template <typename T> bool chkmin(T &a, T b) { return a < b? a = b, true : false; }
namespace SEGT
{
const int MAXT = 3e6 + 5;
int totN;
int stk[MAXT];
int lc[MAXT], rc[MAXT];
int sz[MAXT];
inline void memory_init()
{
for (int i = MAXT - 1; i >= 1; --i) stk[++totN] = i;
}
inline void erase(int cur)
{
lc[cur] = rc[cur] = sz[cur] = 0;
stk[++totN] = cur;
}
void build(int &h, int l, int r, int p)
{
h = stk[totN--];
sz[h] = 1;
if (l == r) return ;
int mid = (l + r) >> 1;
if (p <= mid) build(lc[h], l, mid, p);
else build(rc[h], mid + 1, r, p);
}
int merge(int u, int v)
{
if (!u || !v) return u ^ v;
if (u == v) return 0;
lc[u] = merge(lc[u], lc[v]);
rc[u] = merge(rc[u], rc[v]);
sz[u] += sz[v]; erase(v);
return u;
}
void split(int u, int &v, int k)
{
v = stk[totN--];
if (sz[lc[u]] < k) {
split(rc[u], rc[v], k - sz[lc[u]]);
}
else {
std::swap(rc[u], rc[v]);
if (sz[lc[u]] > k)
split(lc[u], lc[v], k);
}
sz[v] = sz[u] - k, sz[u] = k;
}
int query(int h, int l, int r, int k)
{
if (l == r) return l;
int mid = (l + r) >> 1;
if (sz[lc[h]] >= k) return query(lc[h], l, mid, k);
else return query(rc[h], mid + 1, r, k - sz[lc[h]]);
}
}
const int MAXN = 1e5 + 5;
int N, M;
std::set<PII> S;
int rt[MAXN];
int ty[MAXN];
//split [l, r] -> [l, mid], (mid, r]
void split(int l, int mid, int r)
{
if (ty[l]) {
rt[mid + 1] = rt[l];
SEGT::split(rt[mid + 1], rt[l], r - mid);
}
else {
SEGT::split(rt[l], rt[mid + 1], mid - l + 1);
}
ty[mid + 1] = ty[l];
}
int query(int pos)
{
std::set<PII>::iterator it = S.lower_bound({pos, 0});
if (it->st > pos) --it;
if (ty[it->st]) {
return SEGT::query(rt[it->st], 1, N, it->ed - pos + 1);
}
else {
return SEGT::query(rt[it->st], 1, N, pos - it->st + 1);
}
return assert(false), 0;
}
int main()
{
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
SEGT::memory_init();
N = read(), M = read();
for (int i = 1; i <= N; ++i) {
int p = read();
SEGT::build(rt[i], 1, N, p);
S.insert(PII(i, i));
}
S.insert(PII(N + 1, 0));
for (int c = 1; c <= M; ++c) {
int opt = read(), l = read(), r = read();
std::set<PII>::iterator it = S.lower_bound({l, 0});
if (it->st > l) --it;
static int stk[MAXN]; int len0 = 0;
static PII add[MAXN]; int len1 = 0;
static PII del[MAXN]; int len2 = 0;
for (add[++len1] = {l, r}; it->st <= r; ++it) {
del[++len2] = {it->st, it->ed};
if (it->st < l) {
split(it->st, l - 1, it->ed);
add[++len1] = {it->st, l - 1};
}
else if (it->ed <= r) {
stk[++len0] = it->st;
}
if (it->ed > r) {
split(std::max(it->st, l), r, it->ed);
stk[++len0] = std::max(it->st, l);
add[++len1] = {r + 1, it->ed};
}
}
for (int i = 1; i <= len0; ++i) {
SEGT::merge(rt[l], rt[stk[i]]);
}
for (int i = 1; i <= len2; ++i) {
S.erase(del[i]);
}
for (int i = 1; i <= len1; ++i) {
S.insert(add[i]);
}
ty[l] = opt;
assert(SEGT::sz[rt[l]] == r - l + 1);
}
int pos = read();
printf("%d\n", query(pos));
return 0;
}