[HEOI2016&TJOI2016] 排序(线段树)

4552: [Tjoi2016&Heoi2016]排序

Time Limit: 60 Sec  Memory Limit: 256 MB
Submit: 2703  Solved: 1386
[Submit][Status][Discuss]

Description

在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题

,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排

序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q

位置上的数字。

Input

输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。1 <= n, m <= 10^5第二行为n个整

数,表示1到n的一个全排列。接下来输入m行,每一行有三个整数op, l, r, op为0代表升序排序,op为1代表降序

排序, l, r 表示排序的区间。最后输入一个整数q,q表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5

,1 <= m <= 10^5

Output

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

Sample Input

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

Sample Output

5


这题直接排序肯定T到飞起,然后我们考虑其他做法(废话)

瞎yy仔细分析后这也是个区间问题阿,考虑能不能用线段树维护。

发现说是排序,但是最主要的关系还是之和大小关系有关啊,所以把这个问题抽象成一个01序列

我们可以二分答案,每次check都把所有操作重新进行一遍。这只是个大体思路,我们还要考虑其正确性。

为什么要二分答案,因为把问题转化成01序列后0或1代表该数与目标的大小关系,如果当前的该位置比目标大,那肯定是将区间左断点变大啊

至于决策单调性,博主比较mengbi。有大佬会可以指出阿

现在我们考虑怎么对每个更改进行操作,前面我们说过,01代表的是某个数与目标的大小关系,那我们在排序时只需利用这个性质,对于升序排序,把区间中所有0甩到左边,把1甩到右边,如果降序反过来。

这样我们在二分答案时只需检测询问位置是否为1即可。

对于这这些操作只需维护一棵支持区间覆盖,区间修改的线段数即可。

注意点:在懒标记下传和赋值时,要直接赋值而不是累加。

    要加特判(代码中有注释),因为check()函数中的 change(1,1,n,ql[i],ql[i]+num-1,1)会导致右端点小于左端点

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<vector>
  7 #include<queue>
  8 const int N=1e5+10;
  9 using namespace std;
 10 int mid,sum1,q;
 11 int n,m;
 12 int a[N];
 13 struct node{
 14     int l,r,opt;
 15 }ask[N];
 16 struct NODE{
 17     int l,r,val,f;
 18 }tr[N<<2];
 19 void build(int p,int l,int r){//cout<<"B"<<endl;
 20     tr[p].l=l,tr[p].r=r;
 21     if(l==r){//cout<<"break"<<endl;
 22         tr[p].f=-1;
 23         tr[p].val=(a[l]>=mid);
 24         return ;
 25     }
 26     tr[p].f=-1;
 27     int mid=(l+r)>>1;
 28     build(p<<1,l,mid);
 29     build(p<<1|1,mid+1,r);
 30     tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
 31 }
 32 void down(int p){
 33     if(tr[p].f<0) return ;
 34     tr[p<<1].f=tr[p].f;
 35     tr[p<<1|1].f=tr[p].f;//lanbiaojizhijiefugai
 36     tr[p<<1].val=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].f;
 37     tr[p<<1|1].val=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].f;
 38     tr[p].f=-1;
 39 }
 40 void change(int p,int l,int r,int val){
 41     if(l<=tr[p].l&&tr[p].r<=r){
 42         tr[p].f=val;
 43         tr[p].val=(tr[p].r-tr[p].l+1)*val;//zhijiefuzhi//
 44         return ;
 45     }
 46     if(l>tr[p].r||r<tr[p].l) return ;
 47     down(p);
 48     int mid=(tr[p].l+tr[p].r)>>1;
 49     if(l<=mid) change(p<<1,l,r,val);
 50     if(r>mid) change(p<<1|1,l,r,val);
 51     tr[p].val=tr[p<<1].val+tr[p<<1|1].val;
 52 }
 53 void query(int p,int l,int r){//cout<<p<<" "<<l<<" "<<r<<endl;
 54     if(l<=tr[p].l&&tr[p].r<=r){//cout<<"break"<<endl;
 55         sum1+=tr[p].val;//
 56         return ;
 57     }
 58     if(l>tr[p].r||r<tr[p].l) return ;//特判
 59     down(p);
 60     int mid=(tr[p].l+tr[p].r)>>1;
 61     if(l<=mid)query(p<<1,l,r);
 62     if(r>mid)query(p<<1|1,l,r);
 63 }
 64 bool check(int x){
 65     build(1,1,n);
 66     for(int i=1;i<=m;i++){
 67         sum1=0;query(1,ask[i].l,ask[i].r);
 68         if(ask[i].opt==0){//升序
 69             //cout<<"Q"<<endl;
 70             change(1,ask[i].l,ask[i].r-sum1,0);//
 71             change(1,ask[i].r-sum1+1,ask[i].r,1);//
 72         }
 73         else{
 74             //cout<<"Q"<<endl;
 75             change(1,ask[i].l+sum1,ask[i].r,0);
 76             change(1,ask[i].l,ask[i].l+sum1-1,1);
 77         }
 78     }
 79     //for(int i=1;i<=n;i++) cout<<tr[i].val<<" ";
 80     sum1=0;
 81     query(1,q,q);
 82     //cout<<sum1<<endl;
 83     if(sum1) return true;
 84     else return false;
 85 }
 86 int main(){
 87     //freopen("sort9.in","r",stdin);
 88     scanf("%d%d",&n,&m);
 89     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
 90     for(int i=1;i<=m;i++){
 91         scanf("%d%d%d",&ask[i].opt,&ask[i].l,&ask[i].r);
 92     }
 93     scanf("%d",&q);
 94     int l=1,r=n,ans;
 95     while(l<r){//cout<<"K"<<endl;
 96         mid=(l+r)>>1;//cout<<l<<" "<<r<<" "<<mid<<endl;
 97         //cout<<check(mid)<<endl;
 98         if(check(mid)) ans=mid/*?*/,l=mid+1;
 99         else r=mid;
100     }
101     printf("%d",ans);
102 }

原文地址:https://www.cnblogs.com/leom10/p/11272836.html

时间: 2024-11-09 01:39:16

[HEOI2016&TJOI2016] 排序(线段树)的相关文章

[HEOI2016/TJOI2016]排序 解题报告

[HEOI2016/TJOI2016]排序 题意 给出一个大小为 \(n\) 的排列, 对这个排列进行 \(m\) 次操作, 操作分为以下两种, 0 l r 表示将区间 \([l,r]\) 的数升序排序. 1 l r 表示将区间 \([l,r]\) 的数降序排序. 询问 \(m\) 次操作后下标为 \(q\) 的数字. 思路 不看题解打死也想不出来系列 考虑二分答案. 设当前二分的答案为 \(mid\), 把原排列中 大于等于 \(mid\) 的数标记为 \(1\), 小于 \(mid\) 的数

【线段树合并】【P2824】 [HEOI2016/TJOI2016]排序

Description 给定一个长度为 \(n\) 的排列,有 \(m\) 次操作,每次选取一段局部进行升序或降序排序,问你一波操作后某个位置上的数字是几 Hint \(1~\leq~n,~m~\leq~10^5\) Solution 有两种做法,一种在线一种离线,这里把在线部分讲得更清楚点吧-- 考虑离线算法,我们二分该位置上的答案,将大于该数的元素置为 \(1\),小于该数的元素置为 \(0\),然后模拟所有的排序并检验.由于使用线段树对 \(0/1\) 序列多次局部排序可以做到 \(O(m

[HEOI2016/TJOI2016]排序

4552: [Tjoi2016&Heoi2016]排序 Time Limit: 60 Sec Memory Limit: 256 MB Submit: 2366 Solved: 1188 [Submit][Status][Discuss] Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排 序分为两种:1:(0,l,

计数排序 + 线段树优化 --- Codeforces 558E : A Simple Task

E. A Simple Task Problem's Link: http://codeforces.com/problemset/problem/558/E Mean: 给定一个字符串,有q次操作,每次操作将(l,r)内的字符升序或降序排列,输出q次操作后的字符串. analyse: 基本思想是计数排序. 所谓计数排序,是对一个元素分布较集中的数字集群进行排序的算法,时间复杂度为O(n),但使用条件很苛刻.首先对n个数扫一遍,映射出每个数字出现的次数,然后再O(n)扫一遍处理出:对于数字ai,

CF 558E(A Simple Task-计数排序+线段树)

E. A Simple Task time limit per test 5 seconds memory limit per test 512 megabytes input standard input output standard output This task is very simple. Given a string S of length n and q queries each query is on the format i j k which means sort the

HDU 5195 DZY Loves Topological Sorting (拓扑排序+线段树)

题目地址:HDU 5195 简直受不了了..BC第二题都开始线段树+拓扑排序了... 这题很容易想到拓扑排序过程中贪心,但是贪心容易TLE,所以需要用数据结构去维护,我用的是线段树维护.每次找入度小于等于k的编号最大的点,这样就可以保证字典序一定是最大的. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorith

[Luogu2824] [HEOI2016/TJOI2016]排序

题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q位置上的数字. 输入输出格式 输入格式: 输入数据的第一行为两个整数n和m.n表示序列的长度,m表示局部排序的次数.1 <= n, m <= 10^5第二

LGP2824【[HEOI2016/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$

Codeforces 558E A Simple Task (计数排序&amp;&amp;线段树优化)

题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard output This task is very simple. Given a string S of length n and q queries each quer