【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 ...
随机推荐
- Business Cards UVALive - 4384(画图看图。。)
只能由三种情况 都横着放 都竖着放 横和竖交错放 那就去判断好了... 具体看代码 #include <iostream> #include <cstdio> #inclu ...
- c++11 继承控制:final和override
c++11 继承控制:final和override #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <str ...
- 【BZOJ1922】大陆争霸(最短路)
[BZOJ1922]大陆争霸(最短路) 题面 BZOJ 洛谷 题解 最短路变形题. 定义\(dis\)表示最短路,\(d\)表示最早可以进入当前点的时间.显然\(d=max(max(dis_v,d_v ...
- 【UOJ228】基础数据结构练习题(线段树)
[UOJ228]基础数据结构练习题(线段树) 题面 UOJ 题解 我们来看看怎么开根? 如果区间所有值都相等怎么办? 显然可以直接开根 如果\(max-sqrt(max)=min-sqrt(min)\ ...
- BZOJ 3527 力 | FFT
BZOJ 3527 力 | 分治 题意 给出数组q,$E_i = \sum_{i < j} \frac{q_i}{(i - j) ^ 2} - \sum_{i > j} \frac{q_i ...
- SQL Server 2008定期的备份数据库--差异+完整
https://www.cnblogs.com/l1pe1/p/7885207.html https://www.cnblogs.com/tylerflyn/p/8051398.html https: ...
- [USACO07FEB]牛的词汇The Cow Lexicon
https://daniu.luogu.org/problemnew/show/P2875 dp[i]表示前i-1个字符,最少删除多少个 枚举位置i, 如果打算从i开始匹配, 枚举单词j,计算从i开始 ...
- poj 1419 Graph Coloring
http://poj.org/problem?id=1419 题意: 一张图黑白染色,相邻点不能都染黑色,最多能染几个黑色点 最大点独立集 但是图不能同构为二分图,不能用二分图匹配来做 那就爆搜吧 还 ...
- java抽象类和普通类的区别
1.抽象类不能被实例化. 2.抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态. 3.抽象方法只需申明,而无需实现,抽象类中可以允许普通方法有主体 4.含有抽象方法 ...
- What is an intuitive explanation of the relation between PCA and SVD?
What is an intuitive explanation of the relation between PCA and SVD? 36 FOLLOWERS Last asked: 30 Se ...