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. 130道ASP.NET面试题(二)

    71.什么是反射?答:动态获取程序集信息 72.用Singleton如何写设计模式答:static属性里面new ,构造函数private 73.什么是Application Pool?答:Web应用 ...

  2. [内部类] java笔记之内部类

    1.内部类的分类 2.成员内部类的定义格式 3.一旦使用了内部类,那么生成的class文件长啥样? 其中Body是外部类,Heart是Body的内部类,所以中间有个美元符号$,所以给类进行命名时,不要 ...

  3. 4. 彤哥说netty系列之Java NIO实现群聊(自己跟自己聊上瘾了)

    你好,我是彤哥,本篇是netty系列的第四篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们一起学习了Java中的BIO/NIO/AIO的故事,本章将带着大家一起使 ...

  4. pat 1120 Friend Numbers(20 分)

    1120 Friend Numbers(20 分) Two integers are called "friend numbers" if they share the same ...

  5. iOS界面流畅技巧之微博 Demo 性能优化技巧

    微博 Demo 性能优化技巧 我为了演示 YYKit 的功能,实现了微博和 Twitter 的 Demo,并为它们做了不少性能优化,下面就是优化时用到的一些技巧. 预排版 当获取到 API JSON ...

  6. linux网络测试命令

    一.ping 它通过向目标主机发送一个个数据包以及接受数据包的回应来判断主机和目标主机之间网络连接情况.ping的两个功能:判断网络是否可达.网络性能统计. ping使用的是网络层的ICMP协议. p ...

  7. connected datagram 与TCP连接的区别

    TCP连接流程是TCP协议的一部分,需要经过三次握手.而connected datagram虽然使用了socket的同样的函数connect,但是UDP协议并不包含连接流程,也就是UDP实际上并没有真 ...

  8. Qt Framework 问题之 framework/Versions/A:bundle format unrecognized, invalid, or unsuitable

    在解决标题提到的问题之后,先来介绍下Qt Framework一些基本知识. 基于QT的Mac端工程,在打包时需要对所有需要嵌入到APP的framework及dylib文件进行手动签名处理. 一.签名处 ...

  9. useReducer代替Redux

    创建state.js import React, { createContext,useContext,useReducer } from 'react'; export const countTex ...

  10. Java中的工具类究竟如何命名?

    先来几个例子 JDK自带工具类 Arrays.asList(); Objects.equals(); Collections.sort(); Spring框架工具类 StringUtils.isEmp ...