Time Limit: 10 Sec Memory Limit: 128 MB

Submit: 1363 Solved: 579

[Submit][Status][Discuss]

Description

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

1.查询k在区间内的排名

2.查询区间内排名为k的值

3.修改某一位值上的数值

4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)

5.查询k在区间内的后继(后继定义为大于x,且最小的数)

Input

第一行两个数 n,m 表示长度为n的有序序列和m个操作

第二行有n个数,表示有序序列

下面有m行,opt表示操作标号

若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名

若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数

若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k

若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱

若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

9 6

4 2 2 1 9 4 0 1 1

2 1 4 3

3 4 10

2 1 4 3

1 2 5 9

4 3 9 5

5 2 8 5

Sample Output

2

4

3

4

9

HINT

1.n和m的数据范围:n,m<=50000

2.序列中每个数的数据范围:[0,1e8]

3.虽然原题没有,但事实上5操作的k可能为负数

【题解】



线段树套一个平衡树。

对于线段树的每个节点所代表的区间维护一个平衡树(包含了这个区间里面的所有元素);

用的是SBT(这可能是这份题解唯一的卖点了,我看网上其他人用的都是treap,200+行左右,然而我用了300+行)。

找第k小的数的时候用到了二分答案。

修改操作可以看成是在平衡树里面删掉原来的值。然后再把新的值加入到平衡树中。

这道题的rank是这样的

1 2 3 3 3 5 6 7 8 9

3的rank是3

但是如果查rank4也是3,rank5 也是3,rank3也是3

删除操作有点复杂,因为左右儿子都有的情况需要找个节点来替代,而且这个节点的重复次数也需要改变(同一个数字记录在同一个节点,用”重复”域来表示这个数字重复了几次;

还有,因为同一数字压缩在了同一个节点。所以左旋、右旋代码的size域的修改有所改变

/**************************************************************
Problem: 3196
User: chengchunyang
Language: C++
Result: Accepted
Time:7420 ms
Memory:65572 kb
****************************************************************/ #include <cstdio> const int MAXN = 51000;
const int INF = 1e8 + 10;
//记住要乘16倍就好。
//有MAX*4个是存放树的根节点
//MAX*4*15个是这些树的儿子节点
int root[MAXN * 4], l[MAXN * 4 * 16], r[MAXN * 4 * 16], key[MAXN * 4 * 16], size[MAXN * 4 * 16], cnt = 0;
int a[MAXN], n, m, rank = 0, cf[MAXN * 4 * 16];//cf代表这个数字重复出现了几次
int qianqu, houji; int min(int a, int b)
{
return a > b ? b : a;
} int max(int a, int b)
{
return a > b ? a : b;
} void r_ro(int &t)//右旋代码
{
int k = l[t];
l[t] = r[k];
r[k] = t;
size[k] = size[t];
size[t] = size[l[t]] + size[r[t]] + cf[t];//最后加上的不是1而是cf[t]了。
t = k;
} void l_ro(int &t)//左旋
{
int k = r[t];
r[t] = l[k];
l[k] = t;
size[k] = size[t];
size[t] = size[l[t]] + size[r[t]] + cf[t];
t = k;
} void maintain(int &t, bool flag)//sbt的修复函数
{
if (flag)
{
if (size[l[l[t]]] > size[r[t]])
r_ro(t);
else
if (size[r[l[t]]] > size[r[t]])
l_ro(l[t]), r_ro(t);
else
return;
}
else
{
if (size[r[r[t]]] > size[l[t]])
l_ro(t);
else
if (size[l[r[t]]] > size[l[t]])
r_ro(r[t]), l_ro(t);
else
return;
}
maintain(l[t], true);
maintain(r[t], false);
maintain(t, true);
maintain(t, false);
} void insert(int &t, int x)//插入过程
{
if (!t)
{
t = ++cnt;
size[t] = cf[t] = 1;
l[t] = r[t] = 0;
key[t] = x;
}
else
{
size[t]++;
if (x == key[t])
cf[t]++;
else
if (x < key[t])
insert(l[t], x);
else
insert(r[t], x);
if (x != key[t])
maintain(t, x < key[t]);
}
} void ask_rank(int t, int x)//获取排名,获取的是比它小的数字的个数
{
if (!t)
return;
if (x == key[t])//等于就即加上左子树的大小
rank += size[l[t]];
else
if (x < key[t])
ask_rank(l[t], x);
else
if (x > key[t])//大于
{
rank += size[l[t]] + cf[t];//整个左子树和这个节点都行
ask_rank(r[t], x);
}
} void get_rank(int rt, int begin, int end, int l, int r, int x)
{//线段树,获取这个l,r区间所在的平衡树
if (begin == l && end == r)
{
ask_rank(root[rt], x);
return;
}
int m = (begin + end) >> 1;
if (r <= m)
get_rank(rt << 1, begin, m, l, r, x);
else
if (m < l)
get_rank(rt << 1 | 1, m + 1, end, l, r, x);
else
{
get_rank(rt << 1, begin, m, l, m, x);
get_rank(rt << 1 | 1, m + 1, end, m + 1, r, x);
}
} void build(int rt, int begin, int end, int pos, int x)
{//建线段树,并维护平衡树
insert(root[rt], x);
if (begin == end)
return;
int m = (begin + end) >> 1;
if (pos <= m)
build(rt << 1, begin, m, pos, x);
else
build(rt << 1 | 1, m + 1, end, pos, x);
} void de_lete(int &t, int x)
{//删掉x这个元素
if (!t)
return;
size[t]--;
if (key[t] == x)
{
if (cf[t] > 1)
cf[t]--;
else
{
cf[t] = 0;
bool flag1 = (l[t]>0), flag2 = (r[t]>0);
if (!flag1 && !flag2)
t = 0;
else
if (!flag1 && flag2)
t = r[t];
else
if (flag1 && !flag2)
t = l[t];
else
{
int temp = r[t];
while (l[temp]) temp = l[temp];//这里要用一个节点替代它
int dd = cf[temp] - 1;
temp = r[t];
size[temp] -= dd;//相应的size域都要发生变化
while (l[temp])
{
temp = l[temp];
size[temp] -= dd;
}
key[t] = key[temp];//替代
cf[t] = cf[temp];//那个节点的cf域变成1
cf[temp] = 1;
de_lete(r[t], key[temp]);//下次就会删掉了。
}
}
}
else
if (x < key[t])
de_lete(l[t], x);
else
de_lete(r[t], x);
} void change(int rt, int begin, int end, int pos, int x)
{//更改值实际上就是在平衡树中删掉一个元素,然后再添加进去
de_lete(root[rt], a[pos]);
insert(root[rt], x);
if (begin == end)
return;
int m = (begin + end) >> 1;
if (pos <= m)
change(rt << 1, begin, m, pos, x);
else
change(rt << 1 | 1, m + 1, end, pos, x);
} void ask_before(int t, int x)
{//询问前趋是啥
if (!t)
return;
if (key[t] < x)
{
qianqu = max(qianqu, key[t]);
ask_before(r[t], x);
}
else
if (x <= key[t])
ask_before(l[t], x);
} void get_before(int rt, int begin, int end, int l, int r, int k)
{//获取这个区间所在的平衡树
if (begin == l && end == r)
{
ask_before(root[rt], k);
return;
}
int m = (begin + end) >> 1;
if (r <= m)
get_before(rt << 1, begin, m, l, r, k);
else
if (m < l)
get_before(rt << 1 | 1, m + 1, end, l, r, k);
else
{
get_before(rt << 1, begin, m, l, m, k);
get_before(rt << 1 | 1, m + 1, end, m + 1, r, k);
}
} void ask_houji(int t, int x)
{//询问后继
if (!t)
return;
if (x < key[t])
{
houji = min(houji, key[t]);
ask_houji(l[t], x);
}
else
if (x >= key[t])
ask_houji(r[t], x);
} void get_houji(int rt, int begin, int end, int l, int r, int k)
{//获取这个区间所在的平衡树
if (begin == l && end == r)
{
ask_houji(root[rt], k);
return;
}
int m = (begin + end) >> 1;
if (r <= m)
get_houji(rt << 1, begin, m, l, r, k);
else
if (m < l)
get_houji(rt << 1 | 1, m + 1, end, l, r, k);
else
{
get_houji(rt << 1, begin, m, l, m, k);
get_houji(rt << 1 | 1, m + 1, end, m + 1, r, k);
}
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
build(1, 1, n, i, a[i]);
}
for (int i = 1; i <= m; i++)
{
int opt;
scanf("%d", &opt);
if (opt == 1)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
rank = 1;
get_rank(1, 1, n, l, r, k);
printf("%d\n", rank);
}
else
if (opt == 2)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int le = 0, ri = INF, num;//询问第k小数实际上是二分一个数字,然后判断它的排名
while (le <= ri)//不断调整
{
int m = (le + ri) >> 1;
rank = 1;//我们找的是小于这个数的个数.所以一开始加上1就是这个数的排名了。
get_rank(1, 1, n, l, r, m);
if (rank <= k)//小于等于k就记录这个m为答案。
{//最后记录的是满足排名小于等于k的最大的一个数字.
le = m + 1;
num = m;
}
else
if (rank > k)
ri = m - 1;
}
printf("%d\n", num);
}
else
if (opt == 3)
{
int pos, x;
scanf("%d%d", &pos, &x);
change(1, 1, n, pos, x);
a[pos] = x;
}
else
if (opt == 4)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
qianqu = 0;
get_before(1, 1, n, l, r, k);
printf("%d\n", qianqu);
}
else
if (opt == 5)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
houji = INF;
get_houji(1, 1, n, l, r, k);
printf("%d\n", houji);
}
}
return 0;
}


【42.38%】【BZOJ 3196】二逼平衡树的更多相关文章

  1. BZOJ 3196 二逼平衡树

    Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的 ...

  2. bzoj 3196二逼平衡树 线段树套平衡树

    比较裸的树套树,对于区间K值bz上有一道裸题,详见题解http://www.cnblogs.com/BLADEVIL/p/3455336.html(其实题解也不是很详细) //By BLADEVIL ...

  3. BZOJ 3196 二逼平衡树 ——树套树

    [题目分析] 全靠运气,卡空间. xjb试几次就过了. [代码] #include <cmath> #include <cstdio> #include <cstring ...

  4. bzoj 3196 Tyvj 1730 二逼平衡树(线段树套名次树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1807  Solved: 772[Submit][Stat ...

  5. BZOJ 3196: Tyvj 1730 二逼平衡树( 树套树 )

    这道题做法应该很多吧.... 我用了线段树套treap.... -------------------------------------------------------------------- ...

  6. bzoj 3196 && luogu 3380 JoyOI 1730 二逼平衡树 (线段树套Treap)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3196 题面; 3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Se ...

  7. bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description ...

  8. bzoj 3196: Tyvj 1730 二逼平衡树

    #include<cstdio> #include<ctime> #include<cstdlib> #include<iostream> #defin ...

  9. 【BZOJ 3196】二逼平衡树 线段树套splay 模板题

    我写的是线段树套splay,网上很多人写的都是套treap,然而本蒟蒻并不会treap 奉上sth神犇的模板: //bzoj3196 二逼平衡树,支持修改某个点的值,查询区间第k小值,查询区间某个值排 ...

  10. 【BZOJ-3196】二逼平衡树 线段树 + Splay (线段树套平衡树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2271  Solved: 935[Submit][Stat ...

随机推荐

  1. Ternary Tree

    前一篇文章介绍了Trie树.它实现简单但空间效率低.假设要支持26个英文字母,每一个节点就要保存26个指针,因为节点数组中保存的空指针占用了太多内存.让我来看看Ternary Tree. When y ...

  2. js40---享元模式

    /** * 享元模式是一个为了提高性能(空间复杂度)的设计模式 * 他使用与程序会生产大量的相类似的对象是耗用大量的内存的问题 */ (function(){ /** * 制造商 * 型号 * 拥有者 ...

  3. js12--块作用域函数作用域

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  4. 动态库dll使用module.def文件导出函数(像静态库一样使用)

    1.新建文件module.def. 2.动态库工程上右键->属性->链接器->输入->模块定义文件编辑它写入module.def 3.下面为module.def实例(smart ...

  5. Vue的学习--怎么在vue-cli中写网页

    vue.min.js和vue-cli的区别和联系我现在还是没有太清楚,大概是还没搞清楚export default和new Vue的区别,先浅浅记录一下怎么“用vue-cli来写网页”. “vue-c ...

  6. 看好腾讯,鄙视百度(腾讯的核心竞争力,不是超过10亿的QQ的注册用户,也不是某一项产品、技术方面优势,而是“耐心”:懂得在合适的时间推出合适的产品。”)

    百度,自始至终只是一个低劣的模仿者,且一切向前看,完全违背了一个搜索引擎所应该遵循的基本原则.谁给的钱多就能搜着谁,这跟贩毒有什么区别? 腾讯也在模仿别人,但是,它是模仿然后超越.在中国互联网发展历史 ...

  7. 认识一下JavaScript

    1.什么是JavaScript? JavaScript简称“JS”,应用于HTML和WEB,广泛应用于服务器.PC.笔记本等设备. JavaScript 是 Web 的编程语言. 所有现代的 HTML ...

  8. 【“玲珑杯”ACM比赛 Round #20 H】康娜的数学课

    [链接]http://www.ifrog.cc/acm/problem/1161 [题意] 在这里写题意 [题解] 首先x<l肯定无解; 然后,肯定是要选其中的一些数字的. 而且这些数字肯定是大 ...

  9. 洛谷 P3650 [USACO1.3]滑雪课程设计Ski Course Design

    P3650 [USACO1.3]滑雪课程设计Ski Course Design 题目描述 农民约翰的农场里有N座山峰(1<=N<=1000),每座山都有一个在0到100之间的整数的海拔高度 ...

  10. JIRA6.3.6 安装汉化破解指南

    JIRA6.3.6 安装汉化破解指南 近期试着安装了下JIRA,碰到了些问题.特记录下来,供后来者使用: 1.常规安装 1.1. 下载并安装jira 从官网下载atlassian-jira-6.3.6 ...