个人感觉可能是最不需要脑子写的方法

不过也不太好调

就是用一个普通的线段树维护这个序列,但是对于线段树的每一个区间,再开一个动态开点的权值线段树,里面存储这个区间所有元素值

单点修改只会涉及到log棵权值线段树的单点修改(不用打lazy太棒了 log^2

查询区间内x的排名相当于查询区间内<x的数的个数+1,我们把区间分成log个外层线段树上的区间,然后在每个权值线段树上统计即可,复杂度log^2

查询排名为x的数比较麻烦,我们直接二分,复杂度log^3

查询前驱后继:由于线段树维护的区间,总区间是把这log个区间相加,所以我们再每个权值线段树查询下前驱后继再合并就行,前驱取max,后继取min

至于怎么查询,可以在线段树上二分

代码写的特别乱...

#include <cstdio>
#include <functional>
using namespace std; int l[17000010], r[17000010], tree[17000010], tot;
int rt[200010], init[50010], fuck = 100000000;
int s[10000010], top; int n, m; void chenge(int &x, int cl, int cr, int pos, int val)
{
if (x == 0)
{
if (top > 0) x = s[top--];
else x = ++tot;
}
if (tot % 100000 == 0) fprintf(stderr, "(%d, %d)\n", tot, top);
if (cl == cr) {tree[x] += val; if (tree[x] == 0) s[++top] = x, x = 0; return; }
int mid = (cl + cr) / 2;
if (pos > mid) chenge(r[x], mid + 1, cr, pos, val);
else chenge(l[x], cl, mid, pos, val);
tree[x] = tree[l[x]] + tree[r[x]];
if (l[x] == 0 && r[x] == 0) { s[++top] = x, x = 0; }
} int query(int x, int cl, int cr, int pos)
{
if (x == 0 || cl == cr) return 0;
int mid = (cl + cr) / 2;
if (pos > mid)
return tree[l[x]] + query(r[x], mid + 1, cr, pos);
else return query(l[x], cl, mid, pos);
} int qrange(int x, int cl, int cr, int L, int R)
{
if (x == 0) return 0;
if (R < cl || cr < L) return 0;
if (L <= cl && cr <= R) return tree[x];
int mid = (cl + cr) / 2;
return qrange(l[x], cl, mid, L, R) + qrange(r[x], mid + 1, cr, L, R);
} int getnumber(int x, int cl, int cr, int rank)
{
if (cl == cr) { return cl; }
int mid = (cl + cr) / 2;
if (rank <= tree[l[x]]) return getnumber(l[x], cl, mid, rank);
else return getnumber(r[x], mid + 1, cr, rank - tree[l[x]]);
} int getnumber2(int x, int cl, int cr, int rank)
{
if (cl == cr) { return cl; }
int mid = (cl + cr) / 2;
if (rank <= tree[r[x]]) return getnumber2(r[x], mid + 1, cr, rank);
else return getnumber2(l[x], cl, mid, rank - tree[r[x]]);
} int getprev(int rt, int pos)
{
int tot = qrange(rt, 0, fuck, 0, pos - 1); // [1, pos - 1]内数的个数
if (tot == 0) return -2147483647;
return getnumber(rt, 0, fuck, tot);
} int getnext(int rt, int pos)
{
int tot = qrange(rt, 0, fuck, pos + 1, fuck);
if (tot == 0) return 2147483647;
return getnumber2(rt, 0, fuck, tot);
} //---- 外面线段树 void build(int x, int l, int r)
{
for (int i = l; i <= r; i++)
chenge(rt[x], 0, fuck, init[i], 1);
if (l == r) return;
int mid = (l + r) / 2;
build(x * 2, l, mid);
build(x * 2 + 1, mid + 1, r);
} int qrank(int x, int cl, int cr, int L, int R, int k)
{
if (R < cl || cr < L) return 0;
if (L <= cl && cr <= R) return query(rt[x], 0, fuck, k);
int mid = (cl + cr) / 2;
return qrank(x * 2, cl, mid, L, R, k) + qrank(x * 2 + 1, mid + 1, cr, L, R, k);
} void change(int x, int cl, int cr, int pos, int val)
{
chenge(rt[x], 0, fuck, init[pos], -1);
chenge(rt[x], 0, fuck, val, 1);
if (cl == cr) return;
int mid = (cl + cr) / 2;
if (pos > mid) change(x * 2 + 1, mid + 1, cr, pos, val);
else change(x * 2, cl, mid, pos, val);
} int qprev(int x, int cl, int cr, int L, int R, int k)
{
if (R < cl || cr < L) return -2147483647;
if (L <= cl && cr <= R)
{
int res = getprev(rt[x], k);
return res;
}
int mid = (cl + cr) / 2;
return max(qprev(x * 2, cl, mid, L, R, k), qprev(x * 2 + 1, mid + 1, cr, L, R, k));
} int qnext(int x, int cl, int cr, int L, int R, int k)
{
if (R < cl || cr < L) return 2147483647;
if (L <= cl && cr <= R) return getnext(rt[x], k);
int mid = (cl + cr) / 2;
return min(qnext(x * 2, cl, mid, L, R, k), qnext(x * 2 + 1, mid + 1, cr, L, R, k));
} int main()
{
// printf("%f\n", (3 * sizeof(l) + sizeof(s) + sizeof(rt)) / 1000000.);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &init[i]);
build(1, 1, n);
for (int opd, l, r, k, i = 1; i <= m; i++)
{
scanf("%d%d%d", &opd, &l, &r);
if (opd != 3) scanf("%d", &k);
if (opd == 1)
{
printf("%d\n", qrank(1, 1, n, l, r, k) + 1);
}
if (opd == 2) // query值<k的最大一撮数中最小的一个
{
// for (int i = 0; i <= 10; i++) printf("query(%d) = %d\n", i, qrank(1, 1, n, l, r, i));
int cl = 0, cr = 100000000;
while (cl < cr)
{
int mid = (cl + cr + 1) / 2;
if (qrank(1, 1, n, l, r, mid) < k) cl = mid;
else cr = mid - 1;
}
// int ans = qrank(1, 1, n, l, r, cl);
// cl = 0, cr = 100000000;
// while (cl < cr)
// {
// int mid = (cl + cr) / 2;
// if (qrank(1, 1, n, l, r, mid) >= ans) cr = mid;
// else cl = mid + 1;
// }
printf("%d\n", cl);
}
if (opd == 3)
{
change(1, 1, n, l, r);
init[l] = r;
}
if (opd == 4)
{
printf("%d\n", qprev(1, 1, n, l, r, k));
}
if (opd == 5)
{
printf("%d\n", qnext(1, 1, n, l, r, k));
}
}
return 0;
}

luogu3380 树套树之线段树套线段树的更多相关文章

  1. 「luogu3380」【模板】二逼平衡树(树套树)

    「luogu3380」[模板]二逼平衡树(树套树) 传送门 我写的树套树--线段树套平衡树. 线段树上的每一个节点都是一棵 \(\text{FHQ Treap}\) ,然后我们就可以根据平衡树的基本操 ...

  2. 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题

    “队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄>     线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...

  3. 树剖+线段树||树链剖分||BZOJ1984||Luogu4315||月下“毛景树”

    题面:月下“毛景树” 题解:是道很裸的树剖,但处理的细节有点多(其实是自己线段树没学好).用一个Dfs把边权下移到点权,用E数组记录哪些边被用到了:前三个更新的操作都可以合并起来,可以发现a到b节点间 ...

  4. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  5. BZOJ4881 线段游戏(二分图+树状数组/动态规划+线段树)

    相当于将线段划分成两个集合使集合内线段不相交,并且可以发现线段相交等价于逆序对.也即要将原序列划分成两个单增序列.由dilworth定理,如果存在长度>=3的单减子序列,无解,可以先判掉. 这个 ...

  6. 【BZOJ4817】树点涂色(LCT,线段树,树链剖分)

    [BZOJ4817]树点涂色(LCT,线段树,树链剖分) 题面 BZOJ Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义 ...

  7. [51nod 1766]树上的最远点对 (树的直径+ST表求lca+线段树)

    [51nod 1766]树上的最远点对 (树的直径+ST表求lca+线段树) 题面 给出一棵N个点的树,Q次询问一点编号在区间[l1,r1]内,另一点编号在区间[l2,r2]内的所有点对距离最大值.\ ...

  8. 「雅礼集训 2017 Day2」线段游戏(线段树懒标记“启发式下传”,李超树)

    题面 题解 加入一条线段,可以把它转化为在[L,R]区间内加一条线 y=ax+b (如果原线段与y轴平行,就相当于在{x1}处加一条线 y=max(y1,y2)) 我们可以把它加到线段树上,线段树上每 ...

  9. 树链剖分 - Luogu 3384【模板】树链剖分

    [模板]树链剖分 题目描述 已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操 ...

  10. 数据结构--树(遍历,红黑,B树)

    平时接触树还比较少,写一篇博文来积累一下树的相关知识. 很早之前在数据结构里面学的树的遍历. 前序遍历:根节点->左子树->右子树 中序遍历:左子树->根节点->右子树 后序遍 ...

随机推荐

  1. 开发环境入门 linux基础 (部分) 归档 压缩 Vi编译器 系统分区

    归档 压缩 Vi编译器 系统分区 1.使用cat命令进行文件的纵向合并          1) 使用cat命令实现文件的纵向合并:          a) 例如:将用户信息数据库文件和组信息数据库文件 ...

  2. 第十四章 Spring MVC的工作机制与设计模式(待续)

    Spring MVC的总体设计 Control设计 Model设计 View设计 框架设计的思考 设计模式解析之模版模式

  3. CIA泄露资料分析(黑客工具&技术)—Windows篇

    背景 近期,维基解密曝光了一系列据称来自美国中央情报局(CIA)网络攻击活动的秘密文件,代号为“Vault 7”,被泄露文件的第一部分名为“Year Zero”,共有8761个文件,包含7818个网页 ...

  4. VS2015 MSVC编译FFMPEG

    1.下载安装msys2 http://www.msys2.org/下载msys2 下载安装完成后,在msys2的shell中安装编译FFMPEG必要的命令行工具 pacman -S make gcc ...

  5. 安卓读取SD卡的容量

    在开发中,我们经常会用到SD卡,那么在对SD卡进行读写的时候,我们经常需要判断SD卡的剩余容量是否足够.因此,这次我们来写写获取SD卡容量的程序. 该注意的地方,我都在程序里面有注明了.看程序基本就懂 ...

  6. [Python Study Notes]pd.read_csv()函数读取csv文件绘图

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...

  7. CUDA编程接口:异步并发执行的概念和API

    1.主机和设备间异步执行 为了易于使用主机和设备间的异步执行,一些函数是异步的:在设备完全完成任务前,控制已经返回给主机线程了.它们是: 内核发射; 设备间数据拷贝函数; 主机和设备内拷贝小于64KB ...

  8. thread常用方法

  9. ngx-bootstrap使用04 carousel组件

    1 carousel 是一个通过循环播放图片.文本的幻灯片:就像一个旋转旋转木马一样,但是不支持嵌套使用 2 如何使用 2.1 搭建ngx-bootstrap使用环境 参见博文:点击前往 2.2 在模 ...

  10. ReentrantLock简单实现2

    ReentrantLock: /** * ReentrantLock测试逻辑类 */ public class MyService { private Lock lock = new Reentran ...