排过序的区间可以视为整体,某些区间的某些部分被重新排序相当于分裂和合并
可以用权值线段树维护排序后的区间内的权值。
与此同时我们需要完成线段树和位置的对应
一开始,为了方便,不妨直接开 n 个线段树分别维护每个位置
当对一段区间排序时,可能区间内会有 n 多个线段树和至多两棵要被打折的线段树
对于那 n 多棵线段树直接合并
剩下要被砍断的就 Find 然后 Split + Merge。
为了 Find 应当维护每棵线段树对应区间的左边界
假如现在排序 [L,R] 那么如果被切的区间升序那么需要 FInd_KthMin,K = (R - LBorder + 1)
降序:Find_KthMax,K = (R - LBorder + 1)
接着 Split + Merge 的工作不妨直接把 LBorder~R 给 Merge 出去
一开始会多出 n 棵线段树
这 n 棵线段树被处理掉需要 O ( n log n ) O(n\log n) O(nlogn)
接下来每次排序至多多出两棵新的线段树 O ( m log n ) O(m\log n) O(mlogn)
但是怎么找到这些线段树呢?
可以维护一个 Priority_Queue 记录出现过的线段树的左端点
我更建议用 stl 的 set 比较简洁。另外,不妨直接将左端点位置作为线段树的编号。
但是注意 lower_bound 的时候千万不要用 lower_bound(s.begin(), s.end(), elem)
STL set is using non-random-access iterators. std::set::iterator is bidirectional.
正确复杂度的写法应该是 s.lower_bound(elem)
s.erase(iterator_first, iterator_last) 的写法单次复杂度是 O(n) 级别的。
包括 ++iterator 是 O(log n) 级别的……
但是类似上面那样分析得到,总的复杂度并不会上天、
权值线段树在这里充当了维护桶排的数据结构
没写垃圾回收。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iomanip>
#include<set>
using namespace std;
const int MAXN = 1e5 + 10;
震惊!!const int MAXXN = MAXN << 6;
int n, m, Total = 0;
int LChild[MAXXN], RChild[MAXXN], Size[MAXXN], Root[MAXN];
bool Operated_Type[MAXN];
set<int> SegmentTrees;