【HEOI/TJOI2016】排序

一道神题ORZ,思路真的很妙啊。

正文部分:

题意:

给一个序列,可以对某一个区间升序和降序排序,问你最后数列中第\(Q\)个数是什么?
乍一看貌似毫无思路,于是我们考虑一个更简单的问题:
如果对\(1\)\(01\)序列执行上面的操作,是不是就可以变得简单一点?
设某段区间\([l,r]\)里总共有\(cnt\)个1
那么降序排就是把\(l\sim l+cnt - 1\)修改为\(1\),把\(l+cnt \sim r\)修改为\(0\)
升序排则是把\(r-cnt+1\sim r\)修改为\(1\),\(l\sim r-cnt\)修改为\(0\)
其实一个\(01\)序列有多少个\(1\)就是这个序列的和。
于是这样就变成了一道线段树问题:
区间修改,区间求和

于是我们回归原题,看是否能用一种“\(01\)序列”的方法维护原数列。
答案是可以的。
对于某一个数,我们把大于它的数设为\(1\),小于它的数设为\(0\),于是我们就得到了一个\(01\)序列。
把所有操作全部离线,跑一遍,于是我们就可以二分了。
为什么可以二分?
我们设想一下,如果这个\(01\)序列最后第\(Q\)位是\(1\),说明最后的答案一定比这个数大,否则则一定比这个数小。

而题目又保证了一定为全排列,所以答案肯定只有一个。
于是这道题就变成了一个二分+线段树问题,可以通过了。

#include <bits/stdc++.h>
#define gc getchar
#define il inline
#define lson(x) (x << 1)
#define rson(x) (x << 1 | 1)
const int MAXN = 1e5 + 10;
const int MAXQ = MAXN;
using namespace std;
int n,m,i,j,k,ans,Q;
int a[MAXN];bool b[MAXN];
int tr[MAXN << 2],tag[MAXN << 2];
struct Questions {
	int l,r;bool opt;
	Questions() {l = r = opt = 0;}
	Questions(int L,int R,int Opt) {
		l = L;r = R;opt = Opt;
	}
}query[MAXQ];
il int read() {
	int res = 0;char c;bool sign = 0;
	for(c = gc();!isdigit(c);c = gc()) sign |= c == '-';
	for(;isdigit(c);c = gc()) res = (res << 1) + (res << 3) + (c ^ 48);
	return sign ? -res : res;
}
il void pushup(int num) {
	tr[num] = tr[lson(num)] + tr[rson(num)];
	return;
}
il void init(int num,int l,int r,int val) {
	tr[num] = (r - l + 1) * val;
	tag[num] = val; 
	return;
}
il void pushdown(int num,int l,int r) {
	if(~tag[num]) {
		int mid = l + r >> 1;
		init(lson(num),l,mid,tag[num]);
		init(rson(num),mid + 1,r,tag[num]);
		tag[num] = -1;
	}
	return;
}
void build(int l,int r,int num) {
	if(l == r) {tr[num] = b[l];return;}
	int mid = l + r >> 1;
	build(l,mid,lson(num));
	build(mid + 1,r,rson(num));
	pushup(num);
	return;
}
void modify(int ml,int mr,int l,int r,int num,int val) {
   if(ml <= l && r <= mr) {
        tr[num] = (r - l + 1) * val;
        tag[num] = val;
        return;
    }
    int mid = l + r >> 1;
    pushdown(num,l,r);
    if(ml <= mid) modify(ml,mr,l,mid,lson(num),val);
    if(mid < mr)  modify(ml,mr,mid + 1,r,rson(num),val);
    pushup(num);return; 
}
int Query(int ql,int qr,int l,int r,int num) {
	if(ql <= l && r <= qr) return tr[num];
	pushdown(num,l,r);
	int mid = l + r >> 1,res = 0;
	if(ql <= mid) res += Query(ql,qr,l,mid,lson(num));
	if(mid < qr)  res += Query(ql,qr,mid + 1,r,rson(num));
	return res;
}
il bool judge(int cknum) {
	for(int i = 1;i <= n;i++) {
		b[i] = a[i] >= cknum;
	} 
	memset(tr,0,sizeof(tr));
	memset(tag,-1,sizeof(tag));
	build(1,n,1);
	for(int i = 1;i <= m;i++) {
		int opt = query[i].opt;
		int l = query[i].l;
		int r = query[i].r;
		int cnt = Query(l,r,1,n,1);
		if(!opt) {
			modify(r - cnt + 1,r,1,n,1,1);
			modify(l,r - cnt,1,n,1,0);
		} else {
			modify(l,l + cnt - 1,1,n,1,1);
			modify(l + cnt,r,1,n,1,0);
		}
	}
	return Query(Q,Q,1,n,1);
}
int main() {
	n = read();m = read();
	for(int i = 1;i <= n;i++) a[i] = read();
	for(int i = 1;i <= m;i++) {
		query[i].opt = read();
		query[i].l = read();
		query[i].r = read();
	}		
	Q = read();
	int l = 1,r = n;
	while(l <= r) {
		int mid = l + r >> 1;
		if(judge(mid)) l = mid + 1,ans = mid;
		else r = mid - 1;
	}	
	printf("%d",ans);
	return 0;
}
posted @ 2019-02-10 20:19  _connect  阅读(134)  评论(0编辑  收藏  举报
Live2D