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


线段树合并&复杂度的简单说明: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. kvm虚拟化学习笔记(二)之linux kvm虚拟机安装

    KVM虚拟化学习笔记系列文章列表----------------------------------------kvm虚拟化学习笔记(一)之kvm虚拟化环境安装http://koumm.blog.51 ...

  2. 信号量学习 & 共享内存同步

    刚刚这篇文章学习了共享内存:http://www.cnblogs.com/charlesblc/p/6142139.html 里面也提到了共享内存,自己不进行同步,需要其他手段比如信号量来进行.那么现 ...

  3. 【转载】《Unix网络编程》思维导图

    参考这篇文章,很不错: http://www.cnblogs.com/qiaoconglovelife/p/5734768.html

  4. AE的Annotation学习摘记

    http://xg-357.blog.163.com/blog/static/36263124201151763512894/ IFeatureWorkspaceAnno pFWSAnno = (IF ...

  5. mysql字段A复制到字段B,并替换指定字符

    ',字段a); eg:update `hy_b_pro` set goldWeight=jinJinZhong;

  6. Android 怎样在java/native层改动一个文件的权限(mode)与用户(owner)?

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载.但请保留文章原始出处:          CSDN:http://www.csdn.net        ...

  7. 【iOS系列】-UIScrollView的介绍及结合UIPageControl实现图片播放的实例

    [iOS系列]-UIScrollView的介绍及结合UIPageControl实现图片播放的实例 第一:UIScrollView的常用属性 //表示UIScrollView内容的尺寸,滚动范围 @pr ...

  8. Okapi BM25 (BM stands for Best Matching)

    Okapi BM25 - Wikipedia https://en.wikipedia.org/wiki/Okapi_BM25 In information retrieval, Okapi BM25 ...

  9. openxml in sql server

    OPENXML (Transact-SQL) OPENXML provides a rowset view over an XML document. Because OPENXML is a row ...

  10. div+css布局教程系列2

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...