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

不过也不太好调

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

单点修改只会涉及到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. appium运行时启动失败

    1.检查服务是否开启 2.简单Android设备是否连接成功 3.检查4723端口是否被占用: netstat -ano|findstr '4723' 查到被占用后,找到pid,进入任务管理器查看该p ...

  2. nfs cron shell 作业

    作业一: nginx反向代理三台web服务器,实现负载均衡 所有的web服务共享一台nfs的存储 2台服务器 nginx [lb] :101.200.206.6 nginx [web]:101.200 ...

  3. myeclipse 10破解

    因为笔者的电脑是刚买不久,忘记先给电脑分区,等软年安装差不多了才发现忘记分区,所以就备份了数据,然后分区,结果分区过程中没有异常发生,就没用备用数据,就用分过区的原数据,当时还以为没问题,结果打开my ...

  4. C语言学习笔记--#和##操作符

    1. #运算符 (1)#运算符用于在预处理期将宏的参数转换为字符串 (2)#的转换作用是在预处理期完成的,因此只在宏定义中有效,即其他地方不能用#运算符 (3)用法:#define STRING(x) ...

  5. lucene 5.2.0学习笔记

    package com.bc.cas.manager; import com.bc.cas.dao.BookDao; import com.bc.cas.model.entity.Book; impo ...

  6. 【274】Python 相关问题

    一.中文编码   参考:Python 中文编码 Python中默认的编码格式是 ASCII 格式,在没修改编码格式时无法正确打印汉字,所以在读取中文时会报错. 解决方法为只要在文件开头加入如下代码,任 ...

  7. js实现导航栏的吸顶操作

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...

  8. C++字符串流保存数据

    文件流是以外存文件为输入输出对象的数据流.字符串流是以内存中用户定义的字符数组(字符串)为输入输出对象的. 建立输出字符串流: ostrstream strout(c,sizeof(c));第一个参数 ...

  9. 用StringBuilder来实现经典的反转问题

    import java.util.Scanner; public class Practise03 { public static void main(String[] args) { //键盘录入一 ...

  10. QT中显示图像数据

    博客转载自:https://blog.csdn.net/lg1259156776/article/details/52325091 一般图像数据都是以RGBRGBRGB……字节流的方式(解码完成后的原 ...