BZOJ 4552 排序(二分 || 线段树合并)

来源:互联网 发布:阿里云邮箱app 编辑:程序博客网 时间:2024/06/15 06:51
题意:对一个1~n的全排列, 进行m次局部排序
(0, l, r)将区间[l, r]数字升序排列
(1, l, r)将区间[l, r]数字降序排列

仅一次询问, 询问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<iostream>#include<cstdio>#include<cstring>#include<algorithm>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;}



原创粉丝点击