zoukankan      html  css  js  c++  java
  • 排序[HEOI2016/TJOI2016]

    【题目描述】
    (2016) 年,佳媛姐姐喜欢上了数字序列。因而她经常研究关于序列的一些奇奇怪怪的问题,现在她在研究一个难题,需要你来帮助她。

    这个难题是这样子的:给出一个 (1)(n) 的排列,现在对这个排列序列进行 (m) 次局部排序,排序分为两种:

    • 0 l r 表示将区间 ([l,r]) 的数字升序排序
    • 1 l r 表示将区间 ([l,r]) 的数字降序排序

    注意,这里是对下标在区间 ([l,r]) 内的数排序。
    最后询问第 (q) 位置上的数字。

    【输入格式】
    输入数据的第一行为两个整数 (n)(m)(n) 表示序列的长度,(m) 表示局部排序的次数。

    第二行为 (n) 个整数,表示 (1)(n) 的一个排列。

    接下来输入 (m) 行,每一行有三个整数 ( ext{op},l,r)( ext{op})(0) 代表升序排序,( ext{op})(1) 代表降序排序, (l,r) 表示排序的区间。

    最后输入一个整数 (q),表示排序完之后询问的位置。

    【输出格式】
    输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第 (q) 位置上的数字。

    (n,m≤10_5)(1leq qleq n)

    题解

    发现(m)非常大 用一般的排序肯定会超时

    有什么东西处理区间操作比较快呢 当然是线段树啦

    但是线段树并不支持区间排序

    所以我们把问题转换一下:

    如果要我们用线段树排序一个只由0和1组成的序列 显然是可以通过区间求和以及区间修改做到的

    我们可以二分答案(mid) 然后把输入排列中所有大于等于(mid)的置为1 其余的置为0

    然后用线段树来进行这(m)次对于01序列的排序

    排好后 如果位置(q)上的数字为1 则表示(mid)大于等于答案 否则则是(mid)小于答案

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

    代码

    #include <bits/stdc++.h>
    #define lson ind<<1
    #define rson ind<<1|1
    using namespace std;
    
    inline int read() {
    	int x = 0, f = 1; char ch = getchar();
    	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
    	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
    	return x * f;
    }
    
    int n, m, k, a[100005], tp[100005], le[100005], ri[100005], tmp[100005]; 
    
    namespace Segtree{
    	struct segtree{
    		int l, r, sum, tag;
    	} tr[400005];
    	
    	void build(int ind, int l, int r) {
    		tr[ind].l = l; tr[ind].r = r; tr[ind].tag = -1;
    		if (l == r) {
    			tr[ind].sum = tmp[l]; return;
    		}
    		int mid = (l + r) >> 1;
    		build(lson, l, mid); build(rson, mid+1, r);
    		tr[ind].sum = tr[lson].sum + tr[rson].sum;
    	}
    	
    	inline void pushdown(int ind) {
    		if (tr[ind].tag == -1) return; 
    		int v = tr[ind].tag; tr[ind].tag = -1;
    		tr[lson].tag = v; tr[lson].sum = (tr[lson].r - tr[lson].l + 1) * v;
    		tr[rson].tag = v; tr[rson].sum = (tr[rson].r - tr[rson].l + 1) * v;
    	}
    	
    	void update(int ind, int x, int y, int v) {
    		if (x > y) return;
    		int l = tr[ind].l, r = tr[ind].r;
    		if (x <= l && r <= y) {
    			tr[ind].tag = v; tr[ind].sum = (r - l + 1) * v; return;
    		}
    		int mid = (l + r) >> 1;
    		pushdown(ind);
    		if (x <= mid) update(lson, x, y, v);
    		if (mid < y) update(rson, x, y, v);
    		tr[ind].sum = tr[lson].sum + tr[rson].sum;
    	} 
    	
    	int query(int ind, int x, int y) {
    		int l = tr[ind].l, r = tr[ind].r;
    		if (x <= l && r <= y) {
    			return tr[ind].sum;
    		}
    		int mid = (l + r) >> 1, ret = 0;;
    		pushdown(ind);
    		if (x <= mid) ret += query(lson, x, y);
    		if (mid < y) ret += query(rson, x, y);
    		return ret;
    	}
    }
    
    using namespace Segtree;
    
    bool check(int x) {
    	for (int i = 1; i <= n; i++) tmp[i] = (a[i] >= x);
    	build(1, 1, n); 
    	for (int i = 1; i <= m; i++) {
    		int num = query(1, le[i], ri[i]);
    		if (!tp[i]) {
    			update(1, le[i], ri[i]-num, 0); update(1, ri[i] - num + 1, ri[i], 1);  
    		} else {
    			update(1, le[i], le[i] + num - 1, 1); update(1, le[i] + num, ri[i], 0);  
    		}
    	}
    	return query(1, k, k) == 1;
    }
    
    int main() {
    	n = read(); m = read();
    	for (int i = 1; i <= n; i++) a[i] = read();
    	for (int i = 1; i <= m; i++) tp[i] = read(), le[i] = read(), ri[i] = read();
    	k = read();
    	int l = 1, r = n, mid, ans = 0;
    	while (l <= r) {
    		mid = (l + r) >> 1;
    		if (check(mid)) {
    			ans = mid; l = mid + 1;
    		} else r = mid - 1;
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    linq 基本用法
    string.Substring,string.Concat的用法
    用.NET提供的Mail来发邮件
    委比
    解决启动IIS发生意外错误 0x8ffe2740
    换手
    用Windows Live Writer 2009 发布测试的
    使用matlab工具研究神经网络的简单过程(网络和数据下载)
    拳皇2000连招表
    增加网页曝光率的秘诀
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM55.html
Copyright © 2011-2022 走看看