• 【HEOI/TJOI2016】排序


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

    正文部分:

    题意:

    给一个序列,可以对某一个区间升序和降序排序,问你最后数列中第(Q)个数是什么?
    乍一看貌似毫无思路,于是我们考虑一个更简单的问题:
    如果对(1)(01)序列执行上面的操作,是不是就可以变得简单一点?
    设某段区间([l,r])里总共有(cnt)个1
    那么降序排就是把(lsim l+cnt - 1)修改为(1),把(l+cnt sim r)修改为(0)
    升序排则是把(r-cnt+1sim r)修改为(1),(lsim 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;
    }
    
  • 相关阅读:
    Python Challenge 第 5 关攻略:peak
    Python Challenge 第 4 关攻略:linkedlist
    Python Challenge 第 2 关攻略:ocr
    网站工具
    python使用twisted搭建的一个socket服务
    集合的使用
    开启vmotion,实现虚拟机可以在线迁移的选项
    windows-x64下安装python3.6
    第二天课程的心得体会以及小结
    购物车作业的优化
  • 原文地址:https://www.cnblogs.com/Sai0511/p/10360141.html
Copyright © 2020-2023  润新知