一道十分神奇的线段树题,做法十分的有趣.

前置芝士

  1. 线段树:一个十分基础的数据结构,在这道题中起了至关重要的作用.
  2. 一种基于01串的神奇的二分思想:在模拟赛中出现了这道题,可以先去做一下,这样可能有助于理解.

具体做法

可以发现sort是非常慢的,每次多要\(O(N\log_2N)\),最后的时间复杂度为\(O(MN\log_2N)\),肯定是会T的,而且sort这个东西也不是很好去维护.可以发现,最后需要查询的数只有一个,于是就可以想到离线的做法.\(N\)虽然不小,但其实并不算大,\(O(N\log^2_2N)\)是可以过的,那么有没有这样的方法呢,那自然是有的,所以就要用到01串的一种神奇的用法了.

对于每一次sort的时间复杂度为\(O(N\log_2N)\),那么如果这时一个01串呢,可以发现这样的时间复杂度就可以变为\(O(N)\),而且这时区间覆盖,再用线段树维护就只要\(O(\log_2N)\).



(如这样一个01序列,灰色为1,白色为0,只要查询出区间的和,将最后的这几个覆盖为1,前面覆盖为0,这样为升序,降序同理)

这样就出现了一种单调性,可以发现如果将这个序列中大于等于最终答案的数改为1,小于改为0,那么最后在答案位置的数就一定是1了,反之则一定是0,所以就可以二分答案了,最终的时间复杂度为\(O(M\log^2_2N)\).(二分为\(O(\log_2N)\),每一次check需要\(O(M\log_2N)\))

代码

#include<bits/stdc++.h>
#define rap(i,first,last) for(int i=first;i<=last;++i)
//线段树标准define
#define Lson (now<<1)
#define Rson (now<<1|1)
#define Middle ((left+right)>>1)
#define Left Lson,left,Middle
#define Right Rson,Middle+1,right
#define Now nowleft,nowright
using namespace std;
const int maxN=1e5+7;
int N,M,Q;
int L[maxN],R[maxN];
int arr[maxN];
bool UD[maxN];
struct Lazy//lazy标记
{
int cover;
bool covercheck;//用一个bool型标记这个位置需不需要cover
};
struct Tree//这是一颗资瓷区间覆盖和区间查询和的线段树
{
int sum;
Lazy lazy;
}tree[maxN*4];
void PushUp(int now)
{
tree[now].sum=tree[Lson].sum+tree[Rson].sum;//合并左右子树
}
void Build(int k,int now=1,int left=1,int right=N)//建树
{
tree[now].lazy.covercheck=0;
if(left==right)
{
tree[now].sum=(arr[left]>=k);//在大于等于k时的值为1,小于为0
return;
}
Build(k,Left);
Build(k,Right);
PushUp(now);
}
void Down(int now,int left,int right,int cover)//修改这棵树
{
tree[now].sum=(right-left+1)*cover;
tree[now].lazy.covercheck=1;
tree[now].lazy.cover=cover;
}
void PushDown(int now,int left,int right)//下传标记
{
if(tree[now].lazy.covercheck)//有标记才下传
{
Down(Left,tree[now].lazy.cover);
Down(Right,tree[now].lazy.cover);
tree[now].lazy.covercheck=0;
}
}
void UpData(int nowleft,int nowright,int cover,int now=1,int left=1,int right=N)//区间覆盖部分
{
if(nowright<left||right<nowleft)return;
if(nowleft<=left&&right<=nowright)
{
Down(now,left,right,cover);//直接修改
return;
}
PushDown(now,left,right);//下传标记
UpData(Now,cover,Left);//修改左子树
UpData(Now,cover,Right);//修改右子树
PushUp(now);//合并
}
int Query(int nowleft,int nowright,int now=1,int left=1,int right=N)//查询区间和
{
if(nowright<left||right<nowleft)return 0;
if(nowleft<=left&&right<=nowright)//直接返回
{
return tree[now].sum;
}
PushDown(now,left,right);//下传标记
//值为左右子树的值之和
int result=Query(Now,Left)+Query(Now,Right);
PushUp(now);//需要合并
return result;
}
bool check(int middle)//check的部分
{
Build(middle);//将大于等于middle我改为1,小于为0
int num;
rap(i,1,M)
{
num=Query(L[i],R[i]);//其中1的个数
if(UD[i])
{
//降序修改
UpData(L[i],L[i]+num-1,1);//前num个为1
UpData(L[i]+num,R[i],0);//后面的为0
}
else
{
//升序同理
num=R[i]-L[i]+1-num;
UpData(L[i],L[i]+num-1,0);
UpData(L[i]+num,R[i],1);
}
}
return Query(Q,Q);//返回最终位置的值
}
int getanswer()//二分答案
{
int left=1,right=N;//因为这是一个排列,所以这个数是在1~N的范围内
int answer=-1;
while(left<=right)
{
if(check(Middle))
{
//如果可以就记录答案,并且修改left
answer=Middle;
left=Middle+1;
}
else
{
//不可以就修改right
right=Middle-1;
}
}
return answer;//返回最终答案
}
int main()
{
//离线做法
scanf("%d%d",&N,&M);
rap(i,1,N)scanf("%d",&arr[i]);
rap(i,1,M)scanf("%d%d%d",&UD[i],&L[i],&R[i]);
scanf("%d",&Q);
printf("%d",getanswer());//输出答案
return 0;
}

一种神奇的思路.

「Luogu P2824 [HEOI2016/TJOI2016]排序」的更多相关文章

  1. [Luogu P2824] [HEOI2016/TJOI2016]排序 (线段树+二分答案)

    题面 传送门:https://www.luogu.org/problemnew/show/P2824 Solution 这题极其巧妙. 首先,如果直接做m次排序,显然会T得起飞. 注意一点:我们只需要 ...

  2. luogu P2824 [HEOI2016/TJOI2016]排序

    题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行 ...

  3. Luogu P2824 [HEOI2016/TJOI2016]排序 线段树+脑子

    只会两个$log$的$qwq$ 我们二分答案:设答案为$ans$,则我们把$a[i]<=ans$全部设成$0$,把$a[i]>ans$全部设成$1$,扔到线段树里,这样区间排序(升序)就是 ...

  4. 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告

    P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...

  5. [洛谷P2824][HEOI2016/TJOI2016]排序

    题目大意:一个全排列,两种操作: 1. $0\;l\;r:$把$[l,r]$升序排序2. $1\;l\;r:$把$[l,r]$降序排序 最后询问第$k$位是什么 题解:二分答案,把比这个数大的赋成$1 ...

  6. Luogu 2824 [HEOI2016/TJOI2016]排序

    BZOJ 4552 挺妙的解法. 听说这题直接用一个桶能拿到$80 \ pts$ 发现如果是一个排列的话,要对这个序列排序并不好做,但是假如是$01$序列的话,要对一个区间排序还是很简单的. 发现最后 ...

  7. 洛谷P2824 [HEOI2016/TJOI2016]排序(线段树)

    传送门 这题的思路好清奇 因为只有一次查询,我们考虑二分这个值为多少 将原序列转化为一个$01$序列,如果原序列上的值大于$mid$则为$1$否则为$0$ 那么排序就可以用线段树优化,设该区间内$1$ ...

  8. 洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)

    (另外:题解中有一种思路很高妙而且看上去可以适用一些其他情况的离线方法) 线段树合并&复杂度的简单说明:https://blog.csdn.net/zawedx/article/details ...

  9. 洛谷$P2824\ [HEOI2016/TJOI2016]$ 排序 线段树+二分

    正解:线段树+二分 解题报告: 传送门$QwQ$ 昂着题好神噢我$jio$得$QwQQQQQ$,,, 开始看到长得很像之前考试题的亚子,,,然后仔细康康发现不一样昂$kk$,就这里范围是$[1,n]$ ...

随机推荐

  1. 在 input 上添加图标字体时无法添加的问题

    效果:一个搜索框.如图: 实施过程:一开始,将搜索框分为2部分,用2个 input ,一个 search ,一个 button ,然后给 type="button" 的input ...

  2. python pip 安装常用库如何使用国内镜像源

    最近通过python安装一些常用库发现下载速度特别慢,而且还会出现无法安装等错误,尝试找了一些国内的镜像源,发现阿里云的速度很快,这里做个记录,并分享给大家. 国内镜像源 阿里云:http://mir ...

  3. 6_2 铁轨(UVa514)<栈>

    在一个叫「堆叠市」的城市中有一个有名的火车站.由于地形限制以及经费的关系,火车站及唯一的铁路的样子如下图: 现在火车从A方向来,预定从B方向离开.火车共有N节车厢(N <=1000),并且各车厢 ...

  4. python pylab.plot() 方法使用

    Python 中用pylab模块, pylab.plot() 函数,绘制折线统计图 import pylab as pl x = [, , , ] y = [, , , ] ''' plot参数说明: ...

  5. IIS反向代理配置教程(最终完整版本)

    IIS代理配置教程 插件下载:https://download.csdn.net/download/song_yan_/11996489 一.安装反向代理插件 1.rewrite插件安装 (1) 双击 ...

  6. vue基础总结

    Vue语法: new Vue({ //挂载: el: '#app', //初始化数据: data: {}, //监听 data 的数据变化: watch: { todos: { //深度监视 hand ...

  7. streamsets 安装

    这里有很多坑点 1. 我下载的是rpm 包,full 直接安装, 安装好后,路径在 /opt 目录 2. 启动    -注意报错 2.1  先给他创建 一个 日志目录, 安装好后是不存在这个目录 mk ...

  8. 添加安卓端的User-Agent

    将系统换为Android即可 随机UA UA分析网站 Mozilla/5.0 (Windows NT 6. 4; WOW64) AppleWebKit/537. 36 (KHTML, like Gec ...

  9. Linux - Shebang(#!)

    1. Shebang这个符号通常在Unix系统的脚本中第一行开头中写到,它指明了执行这个脚本文件的解释程序.

  10. L3-023 计算图

    建立结构体保存每个结点的前驱,操作符,来回两遍拓扑排序~ #include<bits/stdc++.h> using namespace std; ; struct node { vect ...