zoukankan      html  css  js  c++  java
  • bzoj4552[HEOI2016/TJOI2016]排序(二分+线段树)

    题目链接

    BZOJ

    洛谷

    解析

    直接对一个序列排序复杂度太高,但对一个(01)序列排序就很简单

    假设我们认定答案就是(x),那么可以将(1)(n)(n)个数分成(3)部分:

    [(1) a[i] < x \ (2) a[i] = x \ (3) a[i] > x ]

    排序时我们只关心三部分之间的大小关系,完全可以不管((1))内部和((3))内部怎么排

    再进一步合并((1))((2)),那么如果排序后(q)位置的数在((1) cup (2))中,那么(x)就可能是答案,如果(q)位置的数在((3))中,显然(x)不是答案,答案一定大于(x)

    于是可以二分这个(x),然后验证即可

    验证的方法是将小于等于(x)的数看作(0),大于(x)的数看作(1),用线段树维护区间内(0)的个数,排序一个区间就先查询区间内(0)的个数(cnt),然后把(l)(l + cnt - 1)修改为(0)(l + cnt)(r)修改为(1),最后查询(q)位置是否为(0)即可

    时间复杂度(O(n log^2 n))

    代码

    PS.以下代码自带大常数,洛谷5000+ms卡着过

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #define MAXN 100005
    
    typedef long long LL;
    struct SegmentTree {
    	int cnt0[MAXN << 2], cover[MAXN << 2];
    	void pushDown(int, int, int);
    	void build(int, int, int);
    	int query(int, int, int, int, int);
    	void update(int, int, int, int, int, int);
    } tree;
    int N, M, Q, op[MAXN], left[MAXN], right[MAXN];
    int a[MAXN], b[MAXN];
    
    bool check(int);
    int main() {
    	std::ios::sync_with_stdio(false);
    	std::cin >> N >> M;
    	for (int i = 1; i <= N; ++i)
    		std::cin >> a[i];
    	for (int i = 1; i <= M; ++i)
    		std::cin >> op[i] >> left[i] >> right[i];
    	std::cin >> Q;
    	int l = 1, r = N;
    	while (l < r) {
    		int mid = (l + r) >> 1;
    		if (check(mid)) r = mid;
    		else l = mid + 1;
    	}
    	std::cout << l << std::endl;
    	
    	return 0;
    }
    void SegmentTree::pushDown(int id, int L, int R) {
    	if (cover[id] == -1) return;
    	int mid = (L + R) >> 1;
    	if (cover[id]) cnt0[id << 1] = cnt0[id << 1 | 1] = 0;
    	else cnt0[id << 1] = mid - L + 1, cnt0[id << 1 | 1] = R - mid;
    	cover[id << 1] = cover[id << 1 | 1] = cover[id];
    	cover[id] = -1;
    }
    void SegmentTree::build(int id, int L, int R) {
    	cover[id] = -1;
    	if (L == R) cnt0[id] = (b[L] == 0);
    	else {
    		int mid = (L + R) >> 1;
    		build(id << 1, L, mid);
    		build(id << 1 | 1, mid + 1, R);
    		cnt0[id] = cnt0[id << 1] + cnt0[id << 1 | 1];
    	}
    }
    void SegmentTree::update(int id, int L, int R, int l, int r, int tp) {
    	if (l > r) return;
    	if (L >= l && R <= r) {
    		cover[id] = tp;
    		cnt0[id] = (tp ? 0 : R - L + 1);
    	} else {
    		pushDown(id, L, R);
    		int mid = (L + R) >> 1;
    		if (l <= mid) update(id << 1, L, mid, l, r, tp);
    		if (r > mid) update(id << 1 | 1, mid + 1, R, l, r, tp);
    		cnt0[id] = cnt0[id << 1] + cnt0[id << 1 | 1];
    	}
    }
    int SegmentTree::query(int id, int L, int R, int l, int r) {
    	if (L >= l && R <= r) return cnt0[id];
    	pushDown(id, L, R);
    	int mid = (L + R) >> 1, res = 0;
    	if (l <= mid) res += query(id << 1, L, mid, l, r);
    	if (r > mid) res += query(id << 1 | 1, mid + 1, R, l, r);
    	return res;
    }
    bool check(int m) {
    	for (int i = 1; i <= N; ++i)
    		b[i] = (a[i] > m);
    	tree.build(1, 1, N);
    	for (int i = 1; i <= M; ++i) {
    		int t = tree.query(1, 1, N, left[i], right[i]);
    		if (op[i]) tree.update(1, 1, N, right[i] - t + 1, right[i], 0), tree.update(1, 1, N, left[i], right[i] - t, 1);
    		else tree.update(1, 1, N, left[i], left[i] + t - 1, 0), tree.update(1, 1, N, left[i] + t, right[i], 1);
    	}
    	return tree.query(1, 1, N, Q, Q);
    }
    
  • 相关阅读:
    POJ 2236
    HDU 6027
    FZU 2278
    FZU 2282
    python之----------字符编码的原理
    python可变数据类型和不可变数据类型
    python的运算符和while循环
    python之字符串的切片
    python数据类型补充
    python的数据类型
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10428145.html
Copyright © 2011-2022 走看看