zoukankan      html  css  js  c++  java
  • [洛谷P2824] 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 位置上的数字。

    样例输入

    6 3
    1 6 2 5 3 4
    0 1 4
    1 3 6
    0 2 4
    3

    样例输出

    5

    说明

    河北省选2016第一天第二题。

    对于 30% 的数据,(n,mleq 1000)

    对于 100% 的数据,(n,mleq 10^5)(1leq qleq n)

    解析

    最简单的做法是每次都排序,但复杂度上显然不能够通过。

    考虑优化一下这个做法。首先发现答案具有单调性,那么就二分一个mid,把大于等于mid的数变为1,小于mid的数变成0。排序时,如果是顺序排序,就可以把所有0放在区间前面,1放在区间后面;逆序同理。这可以用线段树实现。如果最后第q为上是1,说明还可以有更大的mid,否则说明当前的mid过大。

    代码

    #include <iostream>
    #include <cstdio>
    #define N 100002
    using namespace std;
    struct SegmentTree{
    	int dat,add;
    }t[N*4];
    struct event{
    	int op,l,r;
    }p[N];
    int n,m,q,i,a[N],l,r,mid,ans;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void update(int p)
    {
    	t[p].dat=t[p*2].dat+t[p*2+1].dat;
    }
    void spread(int p,int l,int r)
    {
    	if(t[p].add!=-1){
    		int mid=(l+r)/2;
    		t[p*2].add=t[p].add;
    		t[p*2+1].add=t[p].add;
    		t[p*2].dat=t[p].add*(mid-l+1);
    		t[p*2+1].dat=t[p].add*(r-mid);
    		t[p].add=-1;
    	}
    }
    void build(int p,int l,int r,int x)
    {
    	t[p].add=-1;
    	if(l==r){
    		t[p].dat=(a[l]>=x);
    		return;
    	}
    	int mid=(l+r)/2;
    	build(p*2,l,mid,x);
    	build(p*2+1,mid+1,r,x);
    	update(p);
    }
    int ask(int p,int l,int r,int ql,int qr)
    {
        if(ql>qr) return 0;
    	if(ql<=l&&r<=qr) return t[p].dat;
    	int mid=(l+r)/2,ans=0;
    	spread(p,l,r);
    	if(ql<=mid) ans+=ask(p*2,l,mid,ql,qr);
    	if(qr>mid) ans+=ask(p*2+1,mid+1,r,ql,qr);
    	return ans;
    }
    void change(int p,int l,int r,int ql,int qr,int x)
    {
        if(ql>qr) return;
    	if(ql<=l&&r<=qr){
    		t[p].add=x;
    		t[p].dat=(r-l+1)*x;
    		return;
    	}
    	int mid=(l+r)/2;
    	spread(p,l,r);
    	if(ql<=mid) change(p*2,l,mid,ql,qr,x);
    	if(qr>mid) change(p*2+1,mid+1,r,ql,qr,x);
    	update(p);
    }
    bool check(int x)
    {
    	build(1,1,n,x);
    	for(int i=1;i<=m;i++){
    		int cnt=ask(1,1,n,p[i].l,p[i].r);
    		if(p[i].op==0){
    			change(1,1,n,p[i].l,p[i].r-cnt,0);
    			change(1,1,n,p[i].r-cnt+1,p[i].r,1);
    		}
    		else{
    			change(1,1,n,p[i].l,p[i].l+cnt-1,1);
    			change(1,1,n,p[i].l+cnt,p[i].r,0);
    		}
    	}
    	return ask(1,1,n,q,q);
    }
    int main()
    {
    	n=read();m=read();
    	for(i=1;i<=n;i++){
    		a[i]=read();
    		r=max(r,a[i]);
    	}
    	for(i=1;i<=m;i++) p[i].op=read(),p[i].l=read(),p[i].r=read();
    	q=read();
    	while(l<=r){
    		mid=(l+r)/2;
    		if(check(mid)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    让服务器可以下载apk和ipa文件
    MVC第一次访问比较慢的解决方案
    [C#]记录程序耗时的方法【转发】
    uploadify上传带参数及接收参数的方法
    uploadify上传之前判断一个input输入框是否为空
    jgGrid获得的id值是主键的id而不是jqGrid的行号值
    jqGrid删除多行数据问题
    Linux下的微秒级定时器: usleep, nanosleep, select, pselect
    Linux平台延时之sleep、usleep、nanosleep、select比较
    Linux 高精確的時序(sleep, usleep,nanosleep) from:http://blog.sina.com.cn/s/blog_533ab41c0100htae.html
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12190841.html
Copyright © 2011-2022 走看看