3065: 带插入区间K小值

Time Limit: 60 Sec  Memory Limit: 512 MB
Submit: 4696  Solved: 1527
[Submit][Status][Discuss]

Description

从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
请你帮一帮伏特吧。
快捷版题意:带插入、修改的区间k小值在线查询。

Input

第一行一个正整数n,表示原来有n只跳蚤排成一行做早操。
第二行有n个用空格隔开的非负整数,从左至右代表每只跳蚤的弹跳力。
第三行一个正整数q,表示下面有多少个操作。
下面一共q行,一共三种操作对原序列的操作:(假设此时一共m只跳蚤)
1. Q x y k: 询问从左至右第x只跳蚤到从左至右第y只跳蚤中,弹跳力第k小的跳蚤的弹跳力是多少。
    (1 <= x <= y <= m, 1 <= k <= y - x + 1)
2. M x val: 将从左至右第x只跳蚤的弹跳力改为val。
    (1 <= x <= m)
3. I x val: 在从左至右第x只跳蚤的前面插入一只弹跳力为val的跳蚤。即插入后从左至右第x只跳蚤是我刚插入的跳蚤。
    (1 <= x <= m + 1)

为了体现在线操作,设lastAns为上一次查询的时候程序输出的结果,如果之前没有查询过,则lastAns = 0。
则输入的时候实际是:
Q _x _y _k ------> 表示 Q _x^lastAns _y^lastAns _k^lastAns
M _x _val  ------> 表示 M _x^lastAns _val^lastAns
I _x _val  ------> 表示 I _x^lastAns _val^lastAns
简单来说就是操作中输入的整数都要异或上一次询问的结果进行解码。

(祝Pascal的同学早日转C++,就不提供pascal版的描述了。)

Output

对于每个询问输出回答,每行一个回答。

Sample Input

10
10 5 8 28 0 19 2 31 1 22
30
I 6 9
M 1 11
I 8 17
M 1 31
M 6 26
Q 2 7 6
I 23 30
M 31 7
I 22 27
M 26 18
Q 26 17 31
I 5 2
I 18 13
Q 3 3 3
I 27 19
Q 23 23 30
Q 5 13 5
I 3 0
M 15 27
Q 0 28 13
Q 3 29 11
M 2 8
Q 12 5 7
I 30 19
M 11 19
Q 17 8 29
M 29 4
Q 3 0 12
I 7 18
M 29 27

Sample Output

28
2
31
0
14
15
14
27
15
14

HINT

此题作为一个小小的研究来搞吧~做法有很多~不知道这题究竟有多少种做法。

请自觉O(log^2n),我故意卡块状链表,块链A了的请受我深情一拜……

A掉的同学请在Discuss里面简要说下自己的做法吧~

原序列长度 <= 35000

插入个数 <= 35000,修改个数 <= 70000,查询个数 <= 70000  ,0 <= 每时每刻的权值 <= 70000

由于是OJ上的题,所以数据无梯度。为了防止卡OJ,本题只有4组数据。

Source

作者 vfleaking

转自大神:http://hzwer.com/4572.html

替罪羊树是一种不用旋转的平衡树,若一棵子树的左或右子树大小超过其大小55%-80%则暴力重构这棵子树,以此来维护平衡性,每个结点的期望重构次数是logn,实现可以自己脑补一下

在替罪羊树每个结点放一棵包含该子树所有结点的权值线段树,也就是平衡树套权值线段树

1、由于外层是平衡树,那么就能实现插入一个结点:找到它的位置,在根到其路径上所有结点的线段树中插入这个值

2、查询区间第K大:找到这个区间包含若干棵子树,拿出他们的根的权值线段树,一起做个二分

3、修改与插入类似

4、当外层平衡树失衡的时候重构之,将某个子树自下而上线段树合并

然后就是考验数据结构的能力了QAQ

由于内存不够,我们还需要回收垃圾,即对数组的重复使用

写的时候遇到了点麻烦,重构的时候我所有结点暴力插入了,所以我的复杂度是qlog^3n,否则应是qlog^2n

代码:

 #include<bits/stdc++.h>
#define alpha 0.75
#define N 10000005
using namespace std;
inline int read()
{
int x=,f=;;char ch=getchar();
while(ch<''||ch>''){if(ch=='-') f=-; ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int tmp;
int n,m,sz,lastans,root;
int v[],dfn[],rt[],ls[],rs[];
struct seg{
int l,r,sum;
} a[N];
vector<int> rec,t,p;
inline int newnode()
{
if(!rec.size()) return ++sz;
else
{
int k=rec.back();rec.pop_back();
return k;
}
}
inline void reclaim(int &x)
{
if(!x) return;
rec.push_back(x);
reclaim(a[x].l);reclaim(a[x].r);
a[x].sum=;x=;
}
inline void insert(int &k,int l,int r,int val,int f)
{
if(!k) k=newnode();
if(l==r){ a[k].sum+=f; return; }
int mid=(l+r)>>;
if(val<=mid) insert(a[k].l,l,mid,val,f);
else insert(a[k].r,mid+,r,val,f);
a[k].sum=a[a[k].l].sum+a[a[k].r].sum;
if(!a[k].sum)reclaim(k);
}
inline void build(int &k,int l,int r)
{
if(l>r) return;
if(l==r)
{
k=dfn[l];
insert(rt[k],,,v[k],);
return;
}
int mid=(l+r)>>; k=dfn[mid];
build(ls[k],l,mid-); build(rs[k],mid+,r);
for(int i=l;i<=r;i++) insert(rt[k],,,v[dfn[i]],);
}
inline void del(int &x)
{
if(!x) return;
reclaim(rt[x]); del(ls[x]);
p.push_back(x); del(rs[x]);
x=;
}
inline void rebuild(int &x)
{
del(x); int s1=p.size();
for(int i=;i<=s1;i++) dfn[i]=p[i-];
build(x,,s1);
p.clear();
}
inline int modify(int k,int x,int val)
{
insert(rt[k],,,val,);
int t,L=a[rt[ls[k]]].sum;
if(L+==x){ t=v[k];v[k]=val; }
else if(L>=x) t=modify(ls[k],x,val);
else t=modify(rs[k],x-L-,val);
insert(rt[k],,,t,-);
return t;
}
inline void query(int k,int l,int r)
{
int L=a[rt[ls[k]]].sum,R=a[rt[k]].sum;
if(l==&&r==R){ t.push_back(rt[k]);return; }
if(l<=L+&&r>=L+) p.push_back(v[k]);
if(r<=L) query(ls[k],l,r);
else if(l>L+) query(rs[k],l-L-,r-L-);
else
{
if(l<=L)query(ls[k],l,L);
if(R>L+)query(rs[k],,r-L-);
}
}
inline int solve_query(int L,int R,int K)
{
query(root,L,R); K--;
int l=,r=,s1=t.size(),s2=p.size();
while(l<r)
{
int mid=(l+r)>>,sum=;
for(int i=;i<s1;i++) sum+=a[a[t[i]].l].sum;
for(int i=;i<s2;i++)
if(p[i]>=l&&p[i]<=mid) sum++;
if(K<sum)
{
for(int i=;i<s1;i++) t[i]=a[t[i]].l;
r=mid;
}
else
{
for(int i=;i<s1;i++) t[i]=a[t[i]].r;
l=mid+; K-=sum;
}
}
t.clear(); p.clear();
return l;
}
inline void insert(int &k,int x,int val)
{
if(!k)
{
k=++n;
insert(rt[k],,,val,);
v[k]=val;
return;
}
insert(rt[k],,,val,);
int L=a[rt[ls[k]]].sum;
if(L>=x) insert(ls[k],x,val);
else insert(rs[k],x-L-,val);
if(a[rt[k]].sum*alpha>max(a[rt[ls[k]]].sum,a[rt[rs[k]]].sum))
{
if(tmp)
{
if(ls[k]==tmp) rebuild(ls[k]);
else rebuild(rs[k]);
tmp=;
}
}
else tmp=k;
}
int main()
{
n=read();
for(int i=;i<=n;i++) v[i]=read();
for(int i=;i<=n;i++) dfn[i]=i;
build(root,,n);
m=read();
char ch[];int x,y,K;
while(m--)
{
scanf("%s",ch);
x=read();y=read();x^=lastans;y^=lastans;
switch(ch[])
{
case 'Q': K=read();K^=lastans;lastans=solve_query(x,y,K);printf("%d\n",lastans); break;
case 'M': modify(root,x,y); break;
case 'I': tmp=;insert(root,x-,y);if(tmp){ tmp=;rebuild(root); } break;
}
}
return ;
}
  

BZOJ 3065 带插入区间K小值(sag套线段树)的更多相关文章

  1. bzoj 3065: 带插入区间K小值 替罪羊树 && AC300

    3065: 带插入区间K小值 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 1062  Solved: 253[Submit][Status] Des ...

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

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

  3. bzoj 3065: 带插入区间K小值(分块)

    Description 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它 ...

  4. BZOJ 3065 带插入区间K小值

    http://www.lydsy.com/JudgeOnline/problem.php?id=3065 思路:替罪羊树套权值线段树. 当替罪羊树某个子树大于某个比利(比例)时就暴力重构,本题时间复杂 ...

  5. BZOJ 3065 带插入区间K小值 (替罪羊树套线段树)

    毒瘤题.参考抄自博客:hzwer 第一次写替罪羊树,完全是照着题解写的,发现这玩意儿好强啊,不用旋转每次都重构还能nlognnlognnlogn. 还有外面二分和里面线段树的值域一样,那么r = mi ...

  6. 3065: 带插入区间K小值_树套树_替罪羊树_权值线段树

    经过周六一天,周一3个小时的晚自习,周二2个小时的疯狂debug,终于凭借自己切掉了这道树套树题. Code: #include <cstdio> #include <algorit ...

  7. [BZOJ3065]带插入区间K小值 解题报告 替罪羊树+值域线段树

    刚了一天的题终于切掉了,数据结构题的代码真**难调,这是我做过的第一道树套树题,做完后感觉对树套树都有阴影了......下面写一下做题记录. Portal Gun:[BZOJ3065]带插入区间k小值 ...

  8. 【BZOJ】3065: 带插入区间K小值

    http://www.lydsy.com/JudgeOnline/problem.php?id=3065 题意:带插入.修改的区间k小值在线查询.(原序列n<=35000, 询问<=175 ...

  9. 【学习笔记】浅析平衡树套线段树 & 带插入区间K小值

    常见的树套树 一般来说,在嵌套数据结构中,线段树多被作为外层结构使用. 但线段树毕竟是 静态 的结构,导致了一些不便. 下面是一个难以维护的例子: 带插入区间 \(k\) 小值问题 来源:Luogu ...

随机推荐

  1. 来了!GitHub for mobile 发布!iOS beta 版已来,Android 版即将发布

    北京时间 2019 年 11 月 14 日,在 GitHub Universe 2019大会上,GitHub 正式发布了 GitHub for mobile,支持 iOS 与 Android 两大移动 ...

  2. FileStream相关知识分享

    一.如何理解FIleStream 通过前3章的学些,相信大家对于Stream已经有一定的了解,但是又如何去理解FileStream呢?请看下图: 我们磁盘中的任何文件都是通过二进制数组组成,最为直观的 ...

  3. 通过myclipse建立一个简单的Hibernate项目(PS:在单元测试中实现数据的向表的插入)

    Hibernate的主要功能及用法: Ⅰ.Hibernate封装了JDBC,使Java程序员能够以面向对象的思想对数据库进行操作 Ⅱ.Hibernate可以应用于EJB的J2EE架构,完成数据的持久化 ...

  4. CSS RESET —— 浏览器样式重置

    CSS Reset 1. CSS Reset为什么存在? 只要您的客户存在使用不同浏览器(ie,firefox,chrome等)的可能,那你就不得不从完美的理想状态回到现实,因为不同核心的浏览器对CS ...

  5. opencv 3 core组件进阶(2 ROI区域图像叠加&图像混合;分离颜色通道、多通道图像混合;图像对比度,亮度值调整)

    ROI区域图像叠加&图像混合 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp&g ...

  6. vue动态样式设置

    思路: 通过 v-bind:class="true ? style1 : style2 " 配合三元表达式完成样式的切换 具体实现 //return设置控制的参数 //有多个需要样 ...

  7. 扛把子组20191121-3 Final阶段贡献分配规则

    此作业的要求参见http://edu.cnblogs.com/campus/nenu/2019fall/homework/10063 队名:扛把子 组长:孙晓宇 组员:宋晓丽 梁梦瑶 韩昊 刘信鹏 F ...

  8. 将py文件打包为exe文件方法

    前提: pip是依赖python的,首先检查下windows机器上有没有安装python,或者有没有添加到环境变量中,如果都没有需要安装或者加入环境变量 安装pip 下载地址: https://pyp ...

  9. 来理解undefined 和 null 区别

    之前虽然也知道这两个之间的区别,但是让我描述的话,感觉上还是说的不是很清楚.今天也详细看了一次这个知识点,现在来说说这两者间的区别. null: Null类型,代表“空值”,代表一个空对象指针,使用t ...

  10. C#连接SAP【生产系统与ERP对接】

    企业如果上了ERP系统,比如SAP.用友.金蝶或者E10等等,只需要ERP里面提供相应的接口,则可以直接将PMC创建的工单信息抛转至 MTS 系统,当该工单生产完成之后,MTS 将完成数据回传至 ER ...