洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)
(另外:题解中有一种思路很高妙而且看上去可以适用一些其他情况的离线方法)
线段树合并&复杂度的简单说明:https://blog.csdn.net/zawedx/article/details/51818475
调用一次合并函数的时间是常数,
而合并函数每调用一次就会删掉一个点,
所以合并的总代价为删掉的点数和,
或者理解为:
所以好像要合并就不能可持久化?(指保留每一个版本,在合并后仍旧保留合并前的版本)(不确定)
(部分的可持久化应该是可以的,就是在每次合并后保留合并前的版本,但合并前的版本不能再次拿去合并了)
(就是在merge里改一下,如果两个节点同时不为空则新建一个节点而不是直接把其中一个节点当成新节点)
(原因是,这样子相当于每调用一次merge函数从最多删掉一个点变成最多增加一个点;而显然调用merge函数的次数一定是对的,那么增加的空间复杂度也是对的)
此题就是个线段树合并裸题了,额外用一个set维护序列中某一段区间对应的权值线段树的根节点。区间排序就找出set中所有与询问区间相交的区间,将相交部分分裂出来(对应线段树的部分也要分裂出来,可能是前k大或后k大或整一段),然后将剩余部分插回set,最后将所有分出来的相交部分并起来,就得到询问区间排好序的权值线段树。复杂度是n*log
错误记录:空间开成O(n),
没删调试信息的代码:
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
int n,m;
namespace SegT
{
const int N=;
int lc[N],rc[N],dat[N];
int st[N+];int top;
void init() {for(int i=N-;i>=;i--)st[++top]=i;}
int getnode() {int t=st[top--];lc[t]=rc[t]=dat[t]=;return t;}
void delnode(int x) {st[++top]=x;}
#define mid (l+((r-l)>>1))
int L,x;
void _addx(int l,int r,int &num)
{
if(!num) num=getnode();
if(l==r) {dat[num]+=x;return;}
if(L<=mid) _addx(l,mid,lc[num]);
else _addx(mid+,r,rc[num]);
dat[num]=dat[lc[num]]+dat[rc[num]];
}
void addx(int pos,int &num) {L=pos;x=;_addx(,n,num);}
void split(int l,int r,int &num,int &x,int k)
//当前区间为[l,r],将num中的前k小拆出来,放到x中
{
//assert(dat[num]>=k&&k>0);
if(!k) return;
if(!x) x=getnode();
if(l==r) {dat[num]-=k;dat[x]+=k;return;}
int t=dat[lc[num]];
if(t>k) split(l,mid,lc[num],lc[x],k);
else lc[x]=lc[num],lc[num]=;
if(t<k) split(mid+,r,rc[num],rc[x],k-t);
dat[num]=dat[lc[num]]+dat[rc[num]];
dat[x]=dat[lc[x]]+dat[rc[x]];
}
int merge(/*int l,int r,*/int num,int x)
//将x和num并起来
{
if(!x||!num) return x+num;
//if(l==r) {dat[num]+=dat[x];delnode(x);return;}
lc[num]=merge(lc[num],lc[x]);
rc[num]=merge(rc[num],rc[x]);
dat[num]+=dat[x];
delnode(x);
return num;
// if(!lc[num]||!lc[x]) lc[num]=lc[num]+lc[x];
// else merge(l,mid,lc[num],lc[x]);
// if(!rc[num]||!rc[x]) rc[num]=rc[num]+rc[x];
// else merge(mid+1,r,rc[num],rc[x]);
// dat[num]=dat[lc[num]]+dat[rc[num]];
// delnode(x);
}
int _query_kth(int l,int r,int k,int num)
{
if(l==r) return l;
if(dat[lc[num]]>=k) return _query_kth(l,mid,k,lc[num]);
else return _query_kth(mid+,r,k-dat[lc[num]],rc[num]);
}
int query_kth(int k,int num)
{
return _query_kth(,n,k,num);
}
#undef mid
}
struct Dat
{
int r,l,nd,type;//type=0升序,type=1降序
};
bool operator<(const Dat &a,const Dat &b)
{
return a.r<b.r||(a.r==b.r&&a.l<b.l);
}
set<Dat> s;
int split_node(int l,int r)
{
int ansn=,tn;Dat t;
auto it=s.lower_bound((Dat){l,,,});
if(it->l!=l)
{
t=*it;s.erase(it);tn=;
if(t.type==)
{
SegT::split(,n,t.nd,tn,l-t.l);
s.insert((Dat){l-,t.l,tn,});
//ansn=SegT::merge(ansn,t.nd);
s.insert((Dat){t.r,l,t.nd,});
}
else
{
SegT::split(,n,t.nd,tn,t.r-l+);
s.insert((Dat){l-,t.l,t.nd,});
//ansn=SegT::merge(ansn,tn);
s.insert((Dat){t.r,l,tn,});
}
}
it=s.lower_bound((Dat){r,,,});
if(it->r!=r)
{
t=*it;s.erase(it);tn=;
if(t.type==)
{
SegT::split(,n,t.nd,tn,r-t.l+);
s.insert((Dat){t.r,r+,t.nd,});
//ansn=SegT::merge(ansn,tn);
s.insert((Dat){r,t.l,tn,});
}
else
{
SegT::split(,n,t.nd,tn,t.r-r);
s.insert((Dat){t.r,r+,tn,});
//ansn=SegT::merge(ansn,t.nd);
s.insert((Dat){r,t.l,t.nd,});
}
}
while()
{
it=s.lower_bound((Dat){l,,,});
if(it==s.end()||it->l>r) break;
t=*it;s.erase(it);
ansn=SegT::merge(ansn,t.nd);
}
return ansn;
}
//void out()
//{
// for(auto i : s)
// {
// printf("a%d %d %d %d\n",i.r,i.l,i.nd,i.type);
// }
//}
int main()
{
SegT::init();
int t,t2,i,idx,l,r,q;
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&t);
t2=SegT::getnode();
SegT::addx(t,t2);
s.insert((Dat){i,i,t2,});
}
while(m--)
{
scanf("%d%d%d",&idx,&l,&r);
t=split_node(l,r);
s.insert((Dat){r,l,t,idx});
}
scanf("%d",&q);
t=split_node(q,q);
printf("%d",SegT::query_kth(,t));
return ;
}
删了一些奇怪调试代码之后的代码:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline","fast-math","unroll-loops","no-stack-protector")
#pragma GCC diagnostic error "-fwhole-program"
#pragma GCC diagnostic error "-fcse-skip-blocks"
#pragma GCC diagnostic error "-funsafe-loop-optimizations"
#pragma GCC diagnostic error "-std=c++14"
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
int n,m;
namespace SegT
{
const int N=;
int lc[N],rc[N],dat[N];
int st[N+];int top;
void init() {for(int i=N-;i>=;i--)st[++top]=i;}
int getnode() {int t=st[top--];lc[t]=rc[t]=dat[t]=;return t;}
void delnode(int x) {st[++top]=x;}
#define mid (l+((r-l)>>1))
int L,x;
void _addx(int l,int r,int &num)
{
if(!num) num=getnode();
if(l==r) {dat[num]+=x;return;}
if(L<=mid) _addx(l,mid,lc[num]);
else _addx(mid+,r,rc[num]);
dat[num]=dat[lc[num]]+dat[rc[num]];
}
void addx(int pos,int &num) {L=pos;x=;_addx(,n,num);}
void split(int l,int r,int &num,int &x,int k)
//当前区间为[l,r],将num中的前k小拆出来,放到x中
{
if(!k) return;
if(!x) x=getnode();
if(l==r) {dat[num]-=k;dat[x]+=k;return;}
int t=dat[lc[num]];
if(t>k) split(l,mid,lc[num],lc[x],k);
else lc[x]=lc[num],lc[num]=;
if(t<k) split(mid+,r,rc[num],rc[x],k-t);
dat[num]=dat[lc[num]]+dat[rc[num]];
dat[x]=dat[lc[x]]+dat[rc[x]];
}
int merge(int num,int x)
//将x和num并起来
{
if(!x||!num) return x+num;
//if(l==r) {dat[num]+=dat[x];delnode(x);return;}//不用判的
lc[num]=merge(lc[num],lc[x]);
rc[num]=merge(rc[num],rc[x]);
dat[num]+=dat[x];
delnode(x);
return num;
}
int _query_kth(int l,int r,int k,int num)
{
if(l==r) return l;
if(dat[lc[num]]>=k) return _query_kth(l,mid,k,lc[num]);
else return _query_kth(mid+,r,k-dat[lc[num]],rc[num]);
}
int query_kth(int k,int num)
{
return _query_kth(,n,k,num);
}
#undef mid
}
struct Dat
{
int r,l,nd,type;//type=0升序,type=1降序
};
bool operator<(const Dat &a,const Dat &b)
{
return a.r<b.r||(a.r==b.r&&a.l<b.l);
}
set<Dat> s;
int split_node(int l,int r)
{
int ansn=,tn;Dat t;
auto it=s.lower_bound((Dat){l,,,});
if(it->l!=l)
{
t=*it;s.erase(it);tn=;
if(t.type==)
{
SegT::split(,n,t.nd,tn,l-t.l);
s.insert((Dat){l-,t.l,tn,});
s.insert((Dat){t.r,l,t.nd,});
}
else
{
SegT::split(,n,t.nd,tn,t.r-l+);
s.insert((Dat){l-,t.l,t.nd,});
s.insert((Dat){t.r,l,tn,});
}
}
it=s.lower_bound((Dat){r,,,});
if(it->r!=r)
{
t=*it;s.erase(it);tn=;
if(t.type==)
{
SegT::split(,n,t.nd,tn,r-t.l+);
s.insert((Dat){t.r,r+,t.nd,});
s.insert((Dat){r,t.l,tn,});
}
else
{
SegT::split(,n,t.nd,tn,t.r-r);
s.insert((Dat){t.r,r+,tn,});
s.insert((Dat){r,t.l,t.nd,});
}
}
while()
{
it=s.lower_bound((Dat){l,,,});
if(it==s.end()||it->l>r) break;
t=*it;s.erase(it);
ansn=SegT::merge(ansn,t.nd);
}
return ansn;
}
int main()
{
SegT::init();
int t,t2,i,idx,l,r,q;
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
{
scanf("%d",&t);
t2=SegT::getnode();
SegT::addx(t,t2);
s.insert((Dat){i,i,t2,});
}
while(m--)
{
scanf("%d%d%d",&idx,&l,&r);
t=split_node(l,r);
s.insert((Dat){r,l,t,idx});
}
scanf("%d",&q);
t=split_node(q,q);
printf("%d",SegT::query_kth(,t));
return ;
}
洛谷 P2824 [HEOI2016/TJOI2016]排序 (线段树合并)的更多相关文章
- 洛谷$P2824\ [HEOI2016/TJOI2016]$ 排序 线段树+二分
正解:线段树+二分 解题报告: 传送门$QwQ$ 昂着题好神噢我$jio$得$QwQQQQQ$,,, 开始看到长得很像之前考试题的亚子,,,然后仔细康康发现不一样昂$kk$,就这里范围是$[1,n]$ ...
- 洛谷 P2824 [HEOI2016/TJOI2016]排序 解题报告
P2824 [HEOI2016/TJOI2016]排序 题意: 有一个长度为\(n\)的1-n的排列\(m\)次操作 \((0,l,r)\)表示序列从\(l\)到\(r\)降序 \((1,l,r)\) ...
- [Luogu P2824] [HEOI2016/TJOI2016]排序 (线段树+二分答案)
题面 传送门:https://www.luogu.org/problemnew/show/P2824 Solution 这题极其巧妙. 首先,如果直接做m次排序,显然会T得起飞. 注意一点:我们只需要 ...
- BZOJ.4552.[HEOI2016/TJOI2016]排序(线段树合并/二分 线段树)
题目链接 对于序列上每一段连续区间的数我们都可以动态开点建一棵值域线段树.初始时就是\(n\)棵. 对于每次操作,我们可以将\([l,r]\)的数分别从之前它所属的若干段区间中分离出来,合并. 对于升 ...
- 洛谷P2824 [HEOI2016/TJOI2016]排序(线段树)
传送门 这题的思路好清奇 因为只有一次查询,我们考虑二分这个值为多少 将原序列转化为一个$01$序列,如果原序列上的值大于$mid$则为$1$否则为$0$ 那么排序就可以用线段树优化,设该区间内$1$ ...
- [洛谷P2824][HEOI2016/TJOI2016]排序
题目大意:一个全排列,两种操作: 1. $0\;l\;r:$把$[l,r]$升序排序2. $1\;l\;r:$把$[l,r]$降序排序 最后询问第$k$位是什么 题解:二分答案,把比这个数大的赋成$1 ...
- Luogu P2824 [HEOI2016/TJOI2016]排序 线段树+脑子
只会两个$log$的$qwq$ 我们二分答案:设答案为$ans$,则我们把$a[i]<=ans$全部设成$0$,把$a[i]>ans$全部设成$1$,扔到线段树里,这样区间排序(升序)就是 ...
- day 1 晚上 P2824 [HEOI2016/TJOI2016]排序 线段树
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #inclu ...
- [HEOI2016/TJOI2016]排序 线段树+二分
[HEOI2016/TJOI2016]排序 内存限制:256 MiB 时间限制:6000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而 ...
随机推荐
- 分析PMT changed for the ROM:it must be downloaded.升级失败。
应用场景描写叙述: 同样项目不同版本号(不同分支),使用Smart Phone Flash Tool工具交叉升级,出现PMT changed for the ROM;it must be downlo ...
- LightOj 1027 A Dangerous Maze【概率】
题目链接:http://www.lightoj.com/volume_showproblem.php? problem=1027 题意: 你面前有n个门,每一个相应一个数字,若为正xi.代表xi分钟后 ...
- JNI返回复杂对象之中的一个
需求: 首先说需求.近期接手一个项目.要在底层解析二进制数据,数据结构比較负责,因为server是c++server,加之開始没有考虑到移动端开发,所以协议有点扯蛋.大体是这种,一个数据包里面是map ...
- 解决centos yum安装"No package nginx available."问题
问题原因: nginx位于第三方的yum源里面,而不在centos官方yum源里面 解决方法: 安装epel(Extra Packages for Enterprise Linux) a.去epel网 ...
- 初步认识Tensorflow
不多说,直接上干货! TensorFlow 是一个开源软件库,用于使用数据流图进行数值计算.换句话说,即是构建深度学习模型的最佳方式. Tensorflow的官网 https://www.tensor ...
- 怎样快速刪除Word中超链接?
有时我们从网上down了一些资料,存到Word文档里,会发现一些文字和图片带有超链接.这其实是Word自动修改功能引起的麻烦,那么,有什么办法可以把这些超链接快速批量删掉吗? 步骤/方法 1 按键盘上 ...
- 头文件与cpp文件为什么要分开写
最表面的机制是: 头文件是程序的界面(是代码界面),提供给程序员以 类.模版.函数等一系列的声明,让程序员知道应该怎么调用里面的"东西". 从动态链接库的角度看: 头文件提供界面 ...
- Posting array of JSON objects to MVC3 action method via jQuery ajax
Does the model binder not suport arrays of JSON objects? The code below works when sending a single ...
- 现在企业流行的java框架技术
我将简短分析被用于支持这些框架的企业开发环境或工具箱,例如Borland JBuilder,Eclipse以及BEA Workbench.请记住,市场上有许多有关这些开发框架的图书;然而,在任何一篇文 ...
- android使用两种方式注冊receiver
1. package com.example.myreceiver; import android.os.Bundle; import android.app.Activity; import and ...