洛谷 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年,佳媛姐姐喜欢上了数字序列.因而 ...
随机推荐
- 系统优化(一)Maven打包同一个jar有不同的:版本号+时间戳(解决思路)
解决:maven仓库的ear里面有非常多个同样的jar(仅仅是包括不同的:版本号+时间戳) 问题描写叙述: 发现ear里面有非常多个同样的jar,仅仅是包括不同的:版本号+时间戳,例如以下图所看到的: ...
- 原来,表名和字段名不能在pdo中“参数化查询”
https://stackoverflow.com/questions/182287/can-php-pdo-statements-accept-the-table-or-column-name-as ...
- java SE基础(Collection接口概述)
Collection接口相关集成关系例如以下图 1. 关于可迭代接口(Iterable) 可迭代接口仅包括一个方法,返回一个在一组T类型元素上进行迭代的迭代器: public ...
- 块状元素的text-align对齐属性
能够为块状元素(div,h1,h2,form等)内容设置位置text-align:center,left;right;
- 我的家乡:三河古镇已经登上央视CCTV-1新闻联播啦!
在烟雨朦胧时走在古镇的青石街上,别有一番风味!第一幅图为央视的直播车,第二副图为漂亮的三河夜景色!
- 使用HD/IDE层的ioctl接口获取磁盘容量get_hdd_max_sector
利用HD/IDE layer的ioctl函数接口获取HD/IDE的磁盘容量.HDIO_DRIVE_TASK 能发出訪问LBA地址的命令.但不能读写数据. #include <stdio.h&g ...
- BC1.2的一些心得
什么叫DCD DataContact Detect(DCD) 1.首先是DCD 2.然后是Primary detection 3.然后是Secondary detection 检測充电的条件是VBUS ...
- 不等式数列 DP
度度熊最近对全排列特别感兴趣,对于1到n的一个排列,度度熊发现可以在中间根据大小关系插入合适的大于和小于符号(即 '>' 和 '<' )使其成为一个合法的不等式数列.但是现在度度熊手中只有 ...
- 'cmd' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
'cmd' 不是内部或外部命令,也不是可运行的程序或批处理文件. Path 添加 %SystemRoot%/system32;%SystemRoot%;%SystemRoot%/System32/Wb ...
- IP达人启示录(学会经营自己:靠软件来扩大自己的IP,或者获取很大的名声)
在家附近的一个小公园中,一个老人每天晚上都在用水练习书法,他的字写的的确很不错,不懂书法的我,看了就感觉非常的带劲--苍劲有力.今晚再次路过的时候,就有种想和这个老人聊一聊的冲动,那么多年纪了,用书法 ...
