仅一次询问, 询问m次局部排序后第q位置上的数字
1 ≤ n, m, q ≤ 1e5
思路:
这题有个神奇的做法,我们可以直接二分答案x,然后把大于等于x的都看作1,比x小的都看作0插入线段树中,
然后执行m个询问,因为数组中要么是0,要么是1,所以区间排序就简单多了,只要求出区间1的个数,然后把这些1如
果升序放到后面,如果降序放到前面。执行完操作后看下q位置是0还是1即可,如果是1就说明比x大,反之比x小。
时间复杂度O(m log^2(n))
当然还有个麻烦点的做法,但是可以支持在线多次询问。(还没实现)
初始时建立n棵只包含一个点的线段树 (动态开点, 范围均为1~n)
• 用线段树的合并完成排序
• 对于每次局部 ([a, b]) 排序
• 从若干线段树 (将每棵线段树代表的区间插入set, 方便每次局部排序快速的找出第一棵包含[a, b]的线段树)中分裂出在[a, b]中的点, 相当于分裂出线段树中连续的一段
• 将分裂出的若干子线段树合并• 查询相当于查询某个线段树中的第k个值
• 时间复杂度O(n log n)
• 并且支持在线多个询问及局部排序
代码:
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
int tree[maxn*4], set[maxn*4], a[maxn];
int cmd[maxn], L[maxn], R[maxn];
int n, m, p;
void pushup(int root)
{
tree[root] = tree[root*2]+tree[root*2+1];
}
void pushdown(int root, int l, int r)
{
int mid = (l+r)/2;
if(set[root] != -1)
{
set[root*2] = set[root*2+1] = set[root];
tree[root*2] = set[root]*(mid-l+1);
tree[root*2+1] = set[root]*(r-mid);
set[root] = -1;
}
}
void update(int root, int l, int r, int pos, int val)
{
if(l == r)
{
tree[root] = val;
return ;
}
int mid = (l+r)/2;
if(pos <= mid) update(root*2, l, mid, pos, val);
else update(root*2+1, mid+1, r, pos, val);
pushup(root);
}
void Set(int root, int l, int r, int i, int j, int val)
{
if(i <= l && j >= r)
{
set[root] = val;
tree[root] = val*(r-l+1);
return ;
}
pushdown(root, l, r);
int mid = (l+r)/2;
if(i <= mid) Set(root*2, l, mid, i, j, val);
if(j > mid) Set(root*2+1, mid+1, r, i, j, val);
pushup(root);
}
int query(int root, int l, int r, int i, int j)
{
if(i <= l && j >= r)
return tree[root];
pushdown(root, l, r);
int sum = 0;
int mid = (l+r)/2;
if(i <= mid) sum += query(root*2, l, mid, i, j);
if(j > mid) sum += query(root*2+1, mid+1, r, i, j);
return sum;
}
bool judge(int x)
{
memset(set, -1, sizeof(set));
for(int i = 1; i <= n; i++)
{
if(a[i] >= x) update(1, 1, n, i, 1);
else update(1, 1, n, i, 0);
}
for(int i = 1; i <= m; i++)
{
int num = R[i]-L[i]+1;
int big = query(1, 1, n, L[i], R[i]);
if(cmd[i])
{
if(L[i]+big-1 >= L[i])
Set(1, 1, n, L[i], L[i]+big-1, 1);
if(L[i]+big <= R[i])
Set(1, 1, n, L[i]+big, R[i], 0);
}
else
{
if(R[i]-big+1 <= R[i])
Set(1, 1, n, R[i]-big+1, R[i], 1);
if(R[i]-big >= L[i])
Set(1, 1, n, L[i], R[i]-big, 0);
}
}
return query(1, 1, n, p, p);
}
int main(void)
{
while(cin >> n >> m)
{
int l = INF, r = -INF;
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]), l = min(l, a[i]), r = max(r, a[i]);
for(int i = 1; i <= m; i++)
scanf("%d%d%d", &cmd[i], &L[i], &R[i]);
scanf("%d", &p);
int ans = a[1];
while(l <= r)
{
int mid = (l+r)/2;
if(judge(mid)) ans = mid, l = mid+1;
else r = mid-1;
}
printf("%d\n", ans);
}
return 0;
}