HDU 5649 (二分 线段树)

题目链接:点击这里

题意:给出一个排列,每次操作[l,r]区间的数原地升序或者降序,询问最后k下标的数。

二分最后的结果mid,然后把比mid大的换成1,小于等于mid的换成0,那么升序降序相当于求出区间中0的个数然后把前半段和后半段分别用0\1覆盖。这样只需要维护一个区间查询,区间修改的线段树即可。

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define pl c<<1
#define pr (c<<1)|1
#define lson tree[c].l,tree[c].mid,pl
#define rson tree[c].mid+1,tree[c].r,pr
#define maxn 100005

struct node {
    int l, r, mid;
    bool zero, one;///全0全1标记
    int num;///0个数
}tree[maxn<<3];
int op[maxn][3];
int a[maxn], b[maxn];
int n, m, k;

void build_tree (int l, int r, int c) {
    tree[c].l = l, tree[c].r = r, tree[c].mid = (l+r)>>1;
    tree[c].zero = tree[c].one = 0;
    if (l == r) {
        tree[c].num = (b[l] == 0);
        return ;
    }
    build_tree (lson);
    build_tree (rson);
    tree[c].num = tree[pl].num + tree[pr].num;
}

void push_down (int c) {
    if (tree[c].l == tree[c].r) return ;
    if (tree[c].zero) {
        tree[pl].zero = tree[pr].zero = 1;
        tree[pl].one = tree[pr].one = 0;
        tree[pl].num = tree[pl].r-tree[pl].l+1;
        tree[pr].num = tree[pr].r-tree[pr].l+1;
    }
    else if (tree[c].one) {
        tree[pl].zero = tree[pr].zero = 0;
        tree[pl].one = tree[pr].one = 1;
        tree[pl].num = tree[pr].num = 0;
    }
}

void update (int l, int r, int c, int x, int y, int op) {
    if (x > y) return ;
    push_down (c);
    if (x == l && y == r) {
        if (op == 0) {
            tree[c].zero = 1;
            tree[c].one = 0;
            tree[c].num = r-l+1;
        }
        else {
            tree[c].zero = 0;
            tree[c].one = 1;
            tree[c].num = 0;
        }
        return ;
    }
    if (tree[c].mid >= y) update (lson, x, y, op);
    else if (tree[c].mid < x) update (rson, x, y, op);
    else {
        update (lson, x, tree[c].mid, op);
        update (rson, tree[c].mid+1, y, op);
    }
    tree[c].num = tree[pl].num+tree[pr].num;
    tree[c].zero = tree[c].one = 0;
}

int query (int l, int r, int c, int x, int y) {
    push_down (c);
    if (l == x && y == r) {
        return tree[c].num;
    }
    if (tree[c].mid >= y) {
        return query (lson, x, y);
    }
    else if (tree[c].mid < x) {
        return query (rson, x, y);
    }
    else {
        return query (lson, x, tree[c].mid)+query (rson, tree[c].mid+1, y);
    }
}

bool ok (int x) {
    for (int i = 1; i <= n; i++) b[i] = (a[i]>x);
    build_tree (1, n, 1);
    for (int i = 1; i <= m; i++) { //cout << i << endl;
        int tot1 = query (1, n, 1, op[i][1], op[i][2]);//0的数量
        int tot2 = op[i][2]-op[i][1]+1-tot1;//1的数量
        if (op[i][0] == 1) {
            update (1, n, 1, op[i][1], op[i][1]+tot2-1, 1);
            update (1, n, 1, op[i][2]-tot1+1, op[i][2], 0);
        }
        else {
            update (1, n, 1, op[i][1], op[i][1]+tot1-1, 0);
            update (1, n, 1, op[i][2]-tot2+1, op[i][2], 1);
        }
    }
    int res = query (1, n, 1, k, k);
    return res^1;
}

int main () {
    //freopen ("more.in", "r", stdin);
    int t;
    scanf ("%d", &t);
    while (t--) {
        scanf ("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) scanf ("%d", &a[i]);
        for (int i = 1; i <= m; i++) scanf ("%d%d%d", &op[i][0], &op[i][1], &op[i][2]);
        scanf ("%d", &k);
        int l = 1, r = n;
        while (r-l > 1) {
            int mid = (l+r)>>1;
            if (ok (mid)) l = mid;
            else r = mid;
        }
        printf ("%d\n", ok (l) ? r : l);
    }
    return 0;
}

你可能感兴趣的:(线段树,二分/迭代)