【Luogu2824】【HEOI2016/TJOI2016】排序 【二分答案+线段树 / 权值线段树分裂&合并】

[ L i n k ] [\frak{Link}] [Link]


排过序的区间可以视为整体,某些区间的某些部分被重新排序相当于分裂和合并
可以用权值线段树维护排序后的区间内的权值。
与此同时我们需要完成线段树和位置的对应


一开始,为了方便,不妨直接开 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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值