【bzoj3065】: 带插入区间K小值 详解——替罪羊套函数式线段树
由于有了区间操作,我们很容易把区间看成一棵平衡树,对他进行插入,那么外面一层就是平衡树了,这就与我们之前所见到的不同了。我们之前所见到的大多数是线段树套平衡树而此题中插入时坐标会改变即必须对其找到合适的顺序,而线段树无疑是不支持动态插入的,他维护的是一个静态区间(因为插入一个点整个区间的二分结构可能全部改变,这用他就无法通过区间二分来维护信息了)。所以说我们必须来进行平衡树套线段树,那么平衡树就需要维护区间,而线段树就需要维护权值,之前我们的树套树,在外边一个静态区间线段树里面每个节点都有这个点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=;
char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<='')
{
s=(s<<)+(s<<)+ch-;
ch=getchar();
}
return s;
}
const double alpha=0.75;
struct Tree
{
Tree *ch[];
int size,l,r,mid;
}*null,*stack[*MAXN],pool[*MAXN],*now[MAXN];
int Now[MAXN],num,Num;
int list[MAXN],len;
int top;
struct ScapeGoat_Tree
{
ScapeGoat_Tree *ch[];
int size,key;
Tree *root;
bool bad()
{
return size*alpha+<ch[]->size||size*alpha+<ch[]->size;
}
void pushup()
{
size=ch[]->size+ch[]->size+;
}
}*Null,*root,*lst[MAXN],node[MAXN];
int sz;
inline void Init()
{
null=pool;
null->size=null->l=null->r=null->mid=;
null->ch[]=null->ch[]=null;
for(int i=;i<(*MAXN);i++)stack[++top]=pool+i;
Null=node;
Null->size=Null->key=;
Null->ch[]=Null->ch[]=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)>>;
p->size=;
p->ch[]=p->ch[]=null;
return p;
}
inline ScapeGoat_Tree *New(int key)
{
ScapeGoat_Tree *p=&node[++sz];
p->ch[]=p->ch[]=Null;
p->size=;
p->key=key;
p->root=null;
return p;
}
void recovery(Tree *p)
{
if(p==null)return;
recovery(p->ch[]);
stack[++top]=p;
recovery(p->ch[]);
}
void travel(ScapeGoat_Tree *p)
{
if(p==Null)return;
travel(p->ch[]);
lst[++len]=p;
list[len]=p->key;
recovery(p->root);
p->root=null;
travel(p->ch[]);
}
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[],key,p->mid+,r);
else ins(p->ch[],key,l,p->mid);
}
ScapeGoat_Tree *divide(int l,int r)
{
if(l>r)return Null;
int mid=(l+r)>>;
for(int i=l;i<=r;i++)ins(lst[mid]->root,list[i],,inf);
lst[mid]->ch[]=divide(l,mid-);
lst[mid]->ch[]=divide(mid+,r);
lst[mid]->pushup();
return lst[mid];
}
void Ins(ScapeGoat_Tree *p,int key,int pos)
{
ins(p->root,key,,inf);
if(p->ch[]->size+==pos)
{
p->key=key;
return;
}
if(p->ch[]->size>=pos)Ins(p->ch[],key,pos);
else Ins(p->ch[],key,pos-p->ch[]->size-);
}
inline void rebuild(ScapeGoat_Tree *&p)
{
len=;
travel(p);
p=divide(,len);
}
ScapeGoat_Tree **insert(ScapeGoat_Tree *&p,int key,int pos)
{
if(p==Null)
{
p=New(key);
ins(p->root,key,,inf);
return &Null;
}
p->size++;
ins(p->root,key,,inf);
ScapeGoat_Tree **ret;
if(p->ch[]->size+>=pos)ret=insert(p->ch[],key,pos);
else ret=insert(p->ch[],key,pos-p->ch[]->size-);
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()
if(p->ch[]->size+==pos)return p->key;
else if(p->ch[]->size>=pos)p=p->ch[];
else pos-=p->ch[]->size+,p=p->ch[];
}
void del(Tree *&p,int key)
{
p->size--;
if(p->l==p->r)
{
if(p->size==)
{
stack[++top]=p;
p=null;
}
return;
}
del(p->ch[p->mid<key],key);
if(p->size==)
{
stack[++top]=p;
p=null;
}
}
void Del(ScapeGoat_Tree *p,int pos,int key)
{
del(p->root,key);
if(p->ch[]->size+==pos)return;
if(p->ch[]->size>=pos)Del(p->ch[],pos,key);
else Del(p->ch[],pos-p->ch[]->size-,key);
}
int rank(Tree *p,int key)
{
if(p==null)return ;
if(p->l==p->r)return ;
if(key<=p->mid)return rank(p->ch[],key);
else return p->ch[]->size+rank(p->ch[],key);
}
void pre(ScapeGoat_Tree *p,int l,int r)
{
if(p==Null)return;
if(l<=&&p->size<=r)
{
now[++num]=p->root;
return;
}
if(l<=p->ch[]->size)pre(p->ch[],l,r);
if(l<=p->ch[]->size+&&r>=p->ch[]->size+)Now[++Num]=p->key;
if(p->ch[]->size+<r)pre(p->ch[],l-p->ch[]->size-,r-p->ch[]->size-);
}
inline int Rank(int x)
{
int ans=;
for(int i=;i<=num;i++)
ans+=rank(now[i],x);
for(int i=;i<=Num;i++)
if(x>Now[i])ans++;
else break;
return ans;
}
int lastans=;
inline void work1()
{
int x=read()^lastans,y=read()^lastans,k=read()^lastans;
if(x>y)x^=y^=x^=y;
num=Num=;
pre(root,x,y);
sort(Now+,Now+Num+);
int l=,r=inf,ans=;
while(l<=r)
{
int mid=(l+r)>>;
int tmp=Rank(mid)+;
if(tmp<=k)
ans=mid,l=mid+;
else
r=mid-;
}
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=;i<=n;i++)
{
list[++len]=read();
lst[len]=New(list[len]);
}
root=divide(,len);
int Q=read();
while(Q--)
{
char s[];
scanf("%s",s);
if(s[]=='Q')work1();
if(s[]=='M')work2();
if(s[]=='I')work3();
}
return ;
}
【bzoj3065】: 带插入区间K小值 详解——替罪羊套函数式线段树的更多相关文章
- [BZOJ3065]带插入区间K小值 解题报告 替罪羊树+值域线段树
刚了一天的题终于切掉了,数据结构题的代码真**难调,这是我做过的第一道树套树题,做完后感觉对树套树都有阴影了......下面写一下做题记录. Portal Gun:[BZOJ3065]带插入区间k小值 ...
- BZOJ3065 带插入区间K小值 || 洛谷P4278
这是一道让我崩溃的题...... 然鹅洛谷上时限被改然后只有20分......好像所有人都被卡了(雾) 由于替罪羊树不是依靠旋转操作而是依靠暴力重构的方式维护树的平衡,所以我们可以考虑使用替罪羊树套区 ...
- 【函数式权值分块】【块状链表】bzoj3065 带插入区间K小值
显然是块状链表的经典题.但是经典做法的复杂度是O(n*sqrt(n)*log^2(n))的,出题人明确说了会卡掉. 于是我们考虑每个块内记录前n个块的权值分块. 查询的时候差分什么的,复杂度就是O(n ...
- bzoj3065: 带插入区间K小值
无聊来写了下 一开始发现树高是O(n)的,然后就MLE了,进去看了下发现没有重构! 看了半天发现调用错了函数 然后进去又发现不满足sz = ch[0]->sz + ch[1]->sz + ...
- 3065: 带插入区间K小值_树套树_替罪羊树_权值线段树
经过周六一天,周一3个小时的晚自习,周二2个小时的疯狂debug,终于凭借自己切掉了这道树套树题. Code: #include <cstdio> #include <algorit ...
- 【BZOJ3065】带插入区间K小值 替罪羊树+权值线段树
[BZOJ3065]带插入区间K小值 Description 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理 ...
- bzoj 3065: 带插入区间K小值 替罪羊树 && AC300
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1062 Solved: 253[Submit][Status] Des ...
- 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树
题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...
- BZOJ 3065 带插入区间K小值(sag套线段树)
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 4696 Solved: 1527[Submit][Status][Di ...
随机推荐
- MT【159】单调有界有极限
已知数列$\{a_n\}$满足:$a_n>0,a_{n+1}+\dfrac{1}{a_n}<2,n\in N^*$.求证:(1)$a_{n+2}<a_{n+1}<2 (n\in ...
- C++中unique函数
目录 介绍 用法举例 数组 vector 介绍 unique是STL比较实用的一个函数.用于"去除"容器内相邻的重复的元素(只保留一个).这里说的去除并不是真正将容器内的重复元素删 ...
- Ansible基础概述
一.Ansible简介 Ansible基于Python语言实现,由paramiko和PyYAML两个关键模块构建.Ansible的编排引擎可以出色地完成配置管理,流程控制,资源部署等多方面工作.Ans ...
- jumpserver 堡垒机搭建
1.摘要 Jumpserver 是一款由python编写开源的跳板机(堡垒机)系统,实现了跳板机应有的功能.基于ssh协议来管理,客户端无需安装agent. 特点: 完全开源,GPL授权 Python ...
- [POI2012]STU-Well
题意翻译 给定一个非负整数序列A,每次操作可以选择一个数然后减掉1,要求进行不超过m次操作使得存在一个Ak=0且max(∣xi−xi−1∣)最小,输出这个最小值以及此时最小的k (1≤n≤1 000 ...
- python学习(23)requests库爬取猫眼电影排行信息
本文介绍如何结合前面讲解的基本知识,采用requests,正则表达式,cookies结合起来,做一次实战,抓取猫眼电影排名信息. 用requests写一个基本的爬虫 排行信息大致如下图 网址链接为ht ...
- 【Asp.net入门4-02】使用Visual Studio调试器
- jenkins集成sonarqube代码审核
目前在持续集成领域,除了后起之秀travis ci,在老牌工具中,最著名的还是非jenkins莫属.本篇文章简单的说一声jenkins与sonarqube的集成来实现代码的静态审核. 在这里不详细罗列 ...
- List保存在ViewState
private List<SYSUAO> UserRoleList { get { return ViewState["UserRoleList"] as List&l ...
- Spring Cloud微服务实战阅读笔记(一) 基础知识
本文系<Spring Cloud微服务实战>作者:翟永超,一书的阅读笔记. 一:基础知识 1:什么是微服务架构 是一种架构设计风格,主旨是将一个原本独立的系统拆分成多个小型服务 ...