(另外:题解中有一种思路很高妙而且看上去可以适用一些其他情况的离线方法)


线段树合并&复杂度的简单说明:https://blog.csdn.net/zawedx/article/details/51818475

调用一次合并函数的时间是常数,
而合并函数每调用一次就会删掉一个点,
所以合并的总代价为删掉的点数和,

或者理解为:

https://wenku.baidu.com/view/88f4e134e518964bcf847c95.html

所以好像要合并就不能可持久化?(指保留每一个版本,在合并后仍旧保留合并前的版本)(不确定)

(部分的可持久化应该是可以的,就是在每次合并后保留合并前的版本,但合并前的版本不能再次拿去合并了)

(就是在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]排序 (线段树合并)的更多相关文章

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

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

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

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

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

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

  4. BZOJ.4552.[HEOI2016/TJOI2016]排序(线段树合并/二分 线段树)

    题目链接 对于序列上每一段连续区间的数我们都可以动态开点建一棵值域线段树.初始时就是\(n\)棵. 对于每次操作,我们可以将\([l,r]\)的数分别从之前它所属的若干段区间中分离出来,合并. 对于升 ...

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

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

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

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

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

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

  8. day 1 晚上 P2824 [HEOI2016/TJOI2016]排序 线段树

    #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #inclu ...

  9. [HEOI2016/TJOI2016]排序 线段树+二分

    [HEOI2016/TJOI2016]排序 内存限制:256 MiB 时间限制:6000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 在2016年,佳媛姐姐喜欢上了数字序列.因而 ...

随机推荐

  1. delphi 修改文件夹名和文件名

    unit Unit1; interface uses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Form ...

  2. [Rust] Setup Rust for WebAssembly

    In order to setup a project we need to install the nightly build of Rust and add the WebAssembly tar ...

  3. 【Java 虚拟机探索之路系列】:JIT编译器

    作者:郭嘉 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells github:https://github.com/AllenWell 为 ...

  4. 工作总结 使用html模板发邮件 前面空一大块

    HTML邮件的本质其实是发送了一个html页面.邮件的空白必然是页面的空白,所以你要找到你发送邮件的html模板所在,然后去掉空白即可,如果这是一个公共文件,需要注意你往往用的只是你的部分,很大程度还 ...

  5. [转] 买彩票的利器--gun

    源链接 还在自己买彩票吗,有个现成的:GNU shuf命令. shuf -i - -n | 这样就会产生两组彩票(1~36个数字任选) 当然还可以派其他用途,比如: shuf -e clubs hea ...

  6. js常用的正则表达操作

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. PHP中常见的header类型

    <?php // 使用 mime_content_type() 查看 $mimetypes=array( 'ez' => 'application/andrew-inset', 'hqx' ...

  8. HelloH5+搭建

    一.所用工具 代理服务器:squid 编辑工具:HBuilder 调试工具:weinre 参考工具:Hello MUI    HelloH5 二.涉及项目 ***-pifa-------------- ...

  9. html5--项目实战-仿天猫(移动端页面)

    html5--项目实战-仿天猫(移动端页面) 总结: 1.标准搜索栏的做法:这里是弹性布局,放大镜和小话筒是background img 2.手机尾部导航做法:这是一个个 li 标签,每个li标签占% ...

  10. bzoj1426 (洛谷P4550) 收集邮票——期望

    题目:https://www.luogu.org/problemnew/show/P4550 推式子……:https://blog.csdn.net/pygbingshen/article/detai ...