本文已参与「新人创作礼」活动,一起开启掘金创作之路。
戳这里,离线做法 给定一个长度为的数组,对个区间 进行排序 求最后 位置的值 排序分(正序和倒序两种)
分析
上面给的链接有离线的做法,算是一个比较套路的做法,用线段树维护序列 可以在 的时间内实现排序,所以通过二分最终答案可以得到值
但是这题强制要求在线呢? 这样的话,我们需要使用数据结构维护这些数的状态
如何维护信息
我们思考这样一个过程: 初始我们有个区间,都是有序的(每个数自成一个区间) 一旦将区间正序排序 那么从 ,每个区间都可能不是之前的数了 既然我们看这些长度为的区间暂时得不到结果,我们就将这些区间合并起来看,假如我们能够在很快的时间内将这些数据合并起来,我们知道了区间长度为 ,又知道了这些数据合并后的信息,我们岂不是就知道了任意位置 的值,比如 位置就是合并后的最小值, 位置就是合并后的最大值,中间的某个位置就是区间的第大
所以我们要维护的信息就是一颗权值线段树! 由于线段树能够合并!我们的信息也满足短时间()处理完成了
维护的过程
我们继续看,举个确切的例子 给了个数组(这里默认顺序从小到大排序)
5 3 8 2 7 6 1 4 初始区间情况为,每个数自成一个集合
[5] [3] [8] [2] [7] [6] [1] [4]
对区间 排序,权值线段树合并后为(如果不会合并的可以看link)
[5] [2, 3, 6, 7, 8] [1] [4]
如果再对区间 排序,合并后为
[5] [2, 3, 6, 7, 8] [1, 4]
到这一步仍然没啥问题,很好的解决了排序的问题 细心的你一定发现了,我们上面排序的区间都没有交集,所以很自然的合并了 但是如果我们要对区间 进行排序呢? 此时发现,区间已经不是由单个小区间组成的了,我们不能直接像上面一样合并区间内的所有小区间 但是我们的思路就是合并啊,这样才能快速得到某个位置的值,我们怎么创造条件让它合并呢? 既然我们可以合并,那么我们是不是也能分裂? 现在考虑分裂!将我们想要的区间从原区间剥离! 我们想要 区间,该区间在现在的区间 中,我们要从区间中中拿出前三个数(区间长度为) 在权值线段树中,前三个数就是左子树中的三个数 所以我们构建一颗新的树,来存剥离的信息,假设存在了新的树 上,原树存在了下标为 的根所在的树上(分裂后区间为),这里可以容易观察到,我们的信息可以存在区间左端点为下标的根所在树上,所以信息存在下标为的位置,这里只是细节,我们继续讨论过程
分裂后,我们得到了两个小区间,我们想要的区间也是由若干个一整个的区间构成,此时就能执行合并了(这里想要的区间是,分裂后想要的区间刚好由一个一整个区间组成) 最后合并的结果为
[5] [2, 3, 6] [7, 8] [1, 4]
假如我们要对区间 排序呢 我们按照上面的思路,发现想要的区间 由
- 区间的一部分
- 区间的一整个
- 区间的一部分组成
我们目的是要让排序区间,由若干个一整个的区间构成 对于区间的一部分,我们根据上面的操作,分裂成 和 对于一整个区间不分裂 对于区间的那一部分,同样分裂成 和 最终我们得到的排序区间由 ,,组成,都是一整个的 所以我们执行合并,完成了排序操作,结果为
[5] [2] [1, 3, 6, 7, 8] [4]
对于倒序排序,影响的只会在分裂的时候,取前个还是取后个的抉择
区间是怎么组成的?
届时排序问题已经解决完了,还剩下如何判断我想要的区间是否要分裂? 我们将某个区间合并后,这些原来的某些点的信息都集合在了某棵树上,这棵树上面说了,存在新区间(排序区间)的左端点为根的树上 所以我们要维护,这些点的信息在哪棵树上,方便判断是否需要分裂
这里介绍一个小清新数据结构,珂朵莉树 在数据随机的情况下,支持区间的操作可以用珂朵莉树在极小常数下完成 这里贴一个珂朵莉树的介绍 这里是链接,现在没有将来可能会有
因为珂朵莉树取一段区间的时候,也需要分裂,所以刚好在珂朵莉树分裂的时候 将维护信息的权值线段树分裂完,合并珂朵莉树的时候,合并权值线段树 珂朵莉树额外记录一个信息,表示正序还是倒序,也就是用于权值线段树分裂的时候取前个还是后个,此时问题解决完毕
我们要找到第个位置是什么树,参考上面的分裂操作 我们将区间按先分裂,之后将区间分裂为和 这样我们直接查管理存储位置信息的权值线段树就能得到这个答案了
代码
//P2824
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 5e6+5;
const ll MOD = 1e9+7;
int N, M, K;
struct Seg {
struct Tr {
int k, l, r;
}tr[MAX_N];
int root[MAX_N];
int rcnt = 0;
int indx = 0;
int rabi[MAX_N+10];
int tt = 0;
int mk() {
if (tt) {
return rabi[tt--];
}
return ++indx;
}
void del(int rt) {
if (tt >= MAX_N) return;
tr[rt].k = tr[rt].l = tr[rt].r = 0;
rabi[++tt] = rt;
}
void push_up(int rt) {
tr[rt].k = tr[tr[rt].l].k + tr[tr[rt].r].k;
}
void update(int& rt, int l, int r, int x, int k) {
if (!rt) rt = mk();
if (l == r) {
tr[rt].k += k;
return;
}
int mid = l + ((r-l)>>1);
if (x <= mid) update(tr[rt].l, l, mid, x, k);
if (x > mid) update(tr[rt].r, mid+1, r, x, k);
push_up(rt);
}
int merge(int x, int y, int l, int r) {
if (!x || !y) return x | y;
if (l == r) {
tr[x].k += tr[y].k;
return x;
}
int mid = l + ((r-l)>>1);
tr[x].l = merge(tr[x].l, tr[y].l, l, mid);
tr[x].r = merge(tr[x].r, tr[y].r, mid+1, r);
del(y);
push_up(x);
return x;
}
void splitPre(int x, int& y, int l, int r, int k) {
if (!x || k == tr[x].k) return;
if (!y) y = mk();
if (l == r) {
tr[y].k = tr[x].k - k;
tr[x].k = k;
return;
}
int mid = l + ((r-l)>>1);
int lsum = tr[tr[x].l].k;
if (lsum >= k) swap(tr[x].r, tr[y].r), splitPre(tr[x].l, tr[y].l, l, mid, k);
else splitPre(tr[x].r, tr[y].r, mid+1, r, k - lsum);
push_up(x);
push_up(y);
}
void splitBack(int x, int& y, int l, int r, int k) {
if (!x || k == tr[x].k) return;
if (!y) y = mk();
if (l == r) {
tr[y].k = tr[x].k - k;
tr[x].k = k;
return;
}
int mid = l + ((r-l)>>1);
int rsum = tr[tr[x].r].k;
if (rsum >= k) swap(tr[x].l, tr[y].l), splitBack(tr[x].r, tr[y].r, mid+1, r, k);
else splitBack(tr[x].l, tr[y].l, l, mid, k - rsum);
push_up(x);
push_up(y);
}
int query(int rt, int l, int r) {
if (l == r) return l;
int mid = l + ((r-l)>>1);
if (tr[tr[rt].l].k) return query(tr[rt].l, l, mid);
else return query(tr[rt].r, mid+1, r);
}
}seg;
struct Node{
int l, r;
mutable int k;
Node(int a = -1, int b = -1, int c = -1) {
l = a, r = b, k = c;
}
bool operator < (const Node& B) const {
return l < B.l;
}
};
typedef set<Node>::iterator sit;
struct ODT {
set<Node> st;
sit split(int pos) {
sit it = st.lower_bound(Node(pos));
if (it != st.end() && it->l == pos) return it;
--it;
Node tmp = *it;
if (tmp.k==0) {
seg.splitPre(seg.root[tmp.l], seg.root[pos], 1, N, pos-tmp.l);
} else {
seg.splitBack(seg.root[tmp.l], seg.root[pos], 1, N, pos-tmp.l);
}
st.erase(it);
st.insert(Node(tmp.l, pos-1, tmp.k));
return st.insert(Node(pos, tmp.r, tmp.k)).first;
}
void insert(Node t) {
st.insert(t);
}
}odt;
int arr[MAX_N];
void solve(){
sc("%d%d", &N, &M);
odt.insert({N+1, N+1, 0});
for (int i = 1; i <= N; ++i) {
sc("%d", &arr[i]);
seg.update(seg.root[i], 1, N, arr[i], 1);
odt.insert({i, i, 0});
}
int opt, l, r;
for (int i = 1; i <= M; ++i) {
sc("%d%d%d", &opt, &l, &r);
sit rx = odt.split(r+1);
sit lx = odt.split(l);
lx->k = opt;
for (sit i = ++lx;i!=rx;++i) {
seg.root[l] = seg.merge(seg.root[l], seg.root[i->l], 1, N);
seg.root[i->l] = 0;
}
odt.st.erase(lx, rx);
}
int x;
sc("%d", &x);
odt.split(x+1);
odt.split(x);
pr("%d\n", seg.query(seg.root[x], 1, N));
}
signed main()
{
#ifndef ONLINE_JUDGE
//FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}