不得不说,做过最爽的树套树————

由于有了区间操作,我们很容易把区间看成一棵平衡树,对他进行插入,那么外面一层就是平衡树了,这就与我们之前所见到的不同了。我们之前所见到的大多数是线段树套平衡树而此题中插入时坐标会改变即必须对其找到合适的顺序,而线段树无疑是不支持动态插入的,他维护的是一个静态区间(因为插入一个点整个区间的二分结构可能全部改变,这用他就无法通过区间二分来维护信息了)。所以说我们必须来进行平衡树套线段树,那么平衡树就需要维护区间,而线段树就需要维护权值,之前我们的树套树,在外边一个静态区间线段树里面每个节点都有这个点dfs序所对应区间里的数的有序排列,在这里我们用平衡树来维护区间,那么对于里面的点,YY一下,也是对应于父子关系的一段区间,但我们无法将里面的点有序化,但是我们得到了一个可视且可用的一段取值区间这样我们就可以用动态开点来维护权值信息,一般的平衡树像Splay Treap都需要旋转操作而每次旋转里面的线段树维护的权值信息都会随着父子关系的改变而改变,每次都需要新的线段树(合并也行),常数大到飞起(Treap插入乘个log,Spaly插入乘个log查询log2,虽然看起来没那么差甚至根由更优,但是想一想常数),那么替罪羊就要来当大佬了(表示不会无旋Treap),这样只有每次重构才会将重建(合并)线段树,大约有㏒₂n次重建。

空间复杂度:替罪羊n;线段树如果不修改  ∑(i=1 ~log2n)n*[log2n-∑(j=1~log2n-i+1)[(2i-1)/2i]],平均每个点152个,

如果修改的话2*n+∑(i=1~log₂n-1)[17-∑(j=1~log₂n-j+2)[(2i-1)/2i]]*n*2,平均每个点280个

这样的话我们就需要回收空间了,不仅是在重建的时候,也必须得是在修改的时候(别问我为什么这么说QAQ),把他维持到152个以内

时间复杂度:一开始的点用O(n+nlog₂²n),每次插入O(log₂²n),修改O(log₂²n),查询:每一层被选中的点和根(不要忘了我们是在分平衡树千万不要忘了中间那个点)的个数不超过四个(这个我不会理性的证,我先感性的证一下:1.你把这棵树建出来,开始在同一深度找,找到五个的时候怎么也找不到2.这么吊的数据结构查询怎么也得是log级别的啊),所以就是O(log₂n+log₂3n),总重建O(log₂n*(n+∑(i=1 ~log2n)n*[log2n-∑(j=1~log2n-i+1)[(2i-1)/2i]]+n*log²n))大约10*log²n*n

所以总时间复杂度:O(q*log₂3n)估大一点200000*173也就是109由于每个点10s也就行了

然而人傻自带大常数........

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define MAXN 140100
#define inf 70000
using namespace std;
inline int read()
{int s=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-48;ch=getchar();}return s;
}
const double alpha=0.75;
struct Tree
{Tree *ch[2];int size,l,r,mid;
}*null,*stack[120*MAXN],pool[120*MAXN],*now[MAXN];
int Now[MAXN],num,Num;
int list[MAXN],len;
int top;
struct ScapeGoat_Tree
{ScapeGoat_Tree *ch[2];int size,key;Tree *root;bool bad(){return size*alpha+5<ch[0]->size||size*alpha+5<ch[1]->size;}void pushup(){size=ch[0]->size+ch[1]->size+1;}
}*Null,*root,*lst[MAXN],node[MAXN];
int sz;
inline void Init()
{null=pool;null->size=null->l=null->r=null->mid=0;null->ch[1]=null->ch[0]=null;for(int i=1;i<(100*MAXN);i++)stack[++top]=pool+i;Null=node;Null->size=Null->key=0;Null->ch[1]=Null->ch[0]=Null;Null->root=null;root=Null;
}
inline Tree *New(int l,int r)
{Tree *p=stack[top--];p->l=l;p->r=r;p->mid=(l+r)>>1;p->size=0;p->ch[0]=p->ch[1]=null;return p;
}
inline ScapeGoat_Tree *New(int key)
{ScapeGoat_Tree *p=&node[++sz];p->ch[0]=p->ch[1]=Null;p->size=1;p->key=key;p->root=null;return p;
}
void recovery(Tree *p)
{if(p==null)return;recovery(p->ch[0]);stack[++top]=p;recovery(p->ch[1]);
}
void travel(ScapeGoat_Tree *p)
{if(p==Null)return;travel(p->ch[0]);lst[++len]=p;list[len]=p->key;recovery(p->root);p->root=null;travel(p->ch[1]);
}
void ins(Tree *&p,int key,int l,int r)
{if(p==null)p=New(l,r);p->size++;if(p->l==p->r)return;if(p->mid<key) ins(p->ch[1],key,p->mid+1,r);else ins(p->ch[0],key,l,p->mid);
}
ScapeGoat_Tree *divide(int l,int r)
{if(l>r)return Null;int mid=(l+r)>>1;for(int i=l;i<=r;i++)ins(lst[mid]->root,list[i],0,inf);lst[mid]->ch[0]=divide(l,mid-1);lst[mid]->ch[1]=divide(mid+1,r);lst[mid]->pushup();return lst[mid];
}
void Ins(ScapeGoat_Tree *p,int key,int pos)
{ins(p->root,key,0,inf);if(p->ch[0]->size+1==pos){p->key=key;return;}if(p->ch[0]->size>=pos)Ins(p->ch[0],key,pos);else Ins(p->ch[1],key,pos-p->ch[0]->size-1);
}
inline void rebuild(ScapeGoat_Tree *&p)
{len=0;travel(p);p=divide(1,len);
}
ScapeGoat_Tree **insert(ScapeGoat_Tree *&p,int key,int pos)
{if(p==Null){p=New(key);ins(p->root,key,0,inf);return &Null;}p->size++;ins(p->root,key,0,inf);ScapeGoat_Tree **ret;if(p->ch[0]->size+1>=pos)ret=insert(p->ch[0],key,pos);else ret=insert(p->ch[1],key,pos-p->ch[0]->size-1);if(p->bad())ret=&p;return ret;
}
inline void Insert(int key,int pos)
{ScapeGoat_Tree **p=insert(root,key,pos);if(*p!=Null)rebuild(*p);
}
inline int find(int pos)
{ScapeGoat_Tree *p=root;while(1)if(p->ch[0]->size+1==pos)return p->key;else if(p->ch[0]->size>=pos)p=p->ch[0];else pos-=p->ch[0]->size+1,p=p->ch[1];
}
void del(Tree *&p,int key)
{p->size--;if(p->l==p->r){if(p->size==0){stack[++top]=p;p=null;}return;}del(p->ch[p->mid<key],key);if(p->size==0){stack[++top]=p;p=null;}
}
void Del(ScapeGoat_Tree *p,int pos,int key)
{del(p->root,key);if(p->ch[0]->size+1==pos)return;if(p->ch[0]->size>=pos)Del(p->ch[0],pos,key);else Del(p->ch[1],pos-p->ch[0]->size-1,key);
}
int rank(Tree *p,int key)
{if(p==null)return 0;if(p->l==p->r)return 0;if(key<=p->mid)return rank(p->ch[0],key);else return p->ch[0]->size+rank(p->ch[1],key);
}
void pre(ScapeGoat_Tree *p,int l,int r)
{if(p==Null)return;if(l<=1&&p->size<=r){now[++num]=p->root; return;}if(l<=p->ch[0]->size)pre(p->ch[0],l,r);if(l<=p->ch[0]->size+1&&r>=p->ch[0]->size+1)Now[++Num]=p->key;if(p->ch[0]->size+1<r)pre(p->ch[1],l-p->ch[0]->size-1,r-p->ch[0]->size-1);
}
inline int Rank(int x)
{int ans=0;for(int i=1;i<=num;i++)ans+=rank(now[i],x);for(int i=1;i<=Num;i++)if(x>Now[i])ans++;else break;return ans;
}
int lastans=0;
inline void work1()
{int x=read()^lastans,y=read()^lastans,k=read()^lastans;if(x>y)x^=y^=x^=y;num=Num=0;pre(root,x,y);sort(Now+1,Now+Num+1);int l=0,r=inf,ans=0;while(l<=r){int mid=(l+r)>>1;int tmp=Rank(mid)+1;if(tmp<=k)ans=mid,l=mid+1;elser=mid-1;}lastans=ans;printf("%d\n",ans);
}
inline void work2()
{int x=read()^lastans,val=read()^lastans;Del(root,x,find(x));Ins(root,val,x);
}
inline void work3()
{int x=read()^lastans,val=read()^lastans;Insert(val,x);
}
int main()
{Init();int n=read();for(int i=1;i<=n;i++){list[++len]=read();lst[len]=New(list[len]);}root=divide(1,len);int Q=read();while(Q--){char s[2];scanf("%s",s);if(s[0]=='Q')work1();if(s[0]=='M')work2();if(s[0]=='I')work3();}return 0;
}

转载于:https://www.cnblogs.com/TSHugh/p/7015734.html

【bzoj3065】: 带插入区间K小值 详解——替罪羊套函数式线段树相关推荐

  1. BZOJ-3065 带插入区间K小值

    替罪羊树套权值线段树,其中替罪羊树可以满足插入的操作. #include <cstdlib> #include <cstdio> #include <algorithm& ...

  2. 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树

    题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...

  3. P4278 带插入区间K小值

    题目链接 题目描述 快捷版题意:带插入.修改的区间k小值在线查询. 输入格式 第一行一个正整数nnn,表示原来有nnn只跳蚤排成一行做早操. 第二行有nnn个用空格隔开的非负整数,从左至右代表每只跳蚤 ...

  4. 【luogu3834】【模板】可持久化线段树 2(主席树),静态区间第K小值

    problem solution 题目:有n个数,多次询问一个区间[L,R]中第k小的值是多少. 思路: 查询[1,n]中的第k小值: 先对数据进行离散化,然后按值域建立线段树,线段树中维护某个值域中 ...

  5. hdu 2665(主席树查询区间k大值)

    先贴我自己写的代码做模板虽然跟原博主没什么两样.(一开始空间开的4*maxn,交到hdu上一直TLE很奇怪) #include<bits/stdc++.h> using namespace ...

  6. Python|线程和进程|阻塞|非阻塞|同步|异步|生成器和协程|资源竞争|进程间通信|aiohttp库|daemon属性值详解|语言基础50课:学习(11)

    文章目录 系列目录 原项目地址 第34课:Python中的并发编程-1 线程和进程 多线程编程 使用 Thread 类创建线程对象 继承 Thread 类自定义线程 使用线程池 守护线程 资源竞争 G ...

  7. Joomla远程代码执行漏洞分析小白版(小宇特详解)

    Joomla远程代码执行漏洞分析小白版(小宇特详解) 今天看了一下2021陇原战役WP,在看web方向的时候,看到pop链,想了解一下,后来又看到了p师傅在15年的一篇文章,在这里记录一下.这里主要是 ...

  8. 数据库中北大学第二章ppt总结和课后习题详解(小宇特详解)

    数据库中北大学第二章ppt总结和课后习题详解(小宇特详解) 第2章 数据模型与系统结构 2.1 数据模型的基本概念 现实世界--具体事物->信息世界--概念模型->机器世界--DBMS支持 ...

  9. nps内网穿透服务器搭建教程(阿里云)-小宇特详解

    nps内网穿透服务器搭建教程(阿里云)-小宇特详解 前期准备 1.一台云服务器 2.配置安全组 3.在自己的电脑上安装nps 云服务器的话自己买,我这里使用的是阿里云 讲一下配置安全组 打开阿里云的官 ...

最新文章

  1. Windows下Unity5x的下载
  2. datagridview取消默认选中_winform datagridview中的 combobox如何选中默认值?
  3. 通过lvm方式扩展swap分区
  4. Linux 常用命令如何使用?
  5. 大数据分析平台的作用有什么
  6. 通过linux内核如何读取pmbus总线上的数据,Linux内核中读写文件数据的方法
  7. oracle 存储过程 转换为mysql存储过程 实例总结 (数据库有感篇一)
  8. 【国内下载Android系统源码的方法】
  9. 华为路由与交换 eSight 基本功能学习笔记
  10. python中开根号函数_用二分法定义平方根函数(Bisection method Square Root Python)
  11. 通信原理、模电——部分英文术语对照表
  12. android雪花飘落动画,Android自定义View——从零开始实现雪花飘落效果
  13. python meizitu
  14. css3动画数字变化,css3动画的更深层次的探究(矩阵变换)
  15. 王者荣耀交流协会第6次Scrum立会
  16. TI单芯片毫米波雷达xWR1642开箱例程
  17. 【正点原子FPGA连载】第四章 Vivado软件的安装和使用 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0
  18. 毫无疑问计算机犯罪是一个很严重的问题英语,英语四级作文高分句型
  19. android studio设置Flavor的dimension注意细节问题。
  20. 微信小程序的分包加载

热门文章

  1. 解决pip3 install waring ‘The script xxx is installed in ‘/home/xxx/bin‘ which is not on PATH‘
  2. Rust 闭包学习 (Fn/FnMut/FnOnce)
  3. android 设置字体大小不随系统大小变化,App字体大小不随系统改变而改变
  4. Go使用qrcode包解析微信和支付宝二维码,生成一个链接(前端拿到链接即可解析成对应的支付二维码)
  5. Spark - Core 随笔
  6. 【VHDL语言学习笔记(二)】 4位向量加法器
  7. linux挂载u盘显示目录忙,linux下挂载u盘及iso8859-1 not found错误解决
  8. 零点定理的奇妙应用:平分面积的直线
  9. 结算时打印购物小票,计算此次获得的会员积分
  10. 黄健翔激情解说--修改版