题面

  Bzoj3196

解析

  线段树和Splay两棵树套在一起,常数直逼inf,但最终侥幸过了

  思路还是比较简单, 在原数组维护一个下标线段树,再在每一个线段树节点,维护一个对应区间的权值Splay。简单说一下操作:

 0.提取区间

  这个操作是1、2、4、5操作的基础,其实也比较容易实现,在线段树中跑一跑,如果询问区间包含了节点覆盖的区间,就在一个数组中存一下节点的编号,然后返回就行

 1.查询区间内k的排名

  提取区间,找到区间内所有的Splay, 分别比k小的数的个数,相加后加一即可

 2.查询区间第k大

  提取区间,找到区间内所有的Splay,因为有多棵Splay,这个操作显然不能像一般的Splay一样查询,只能二分答案,转化为操作一,再check排名就行

 3.单点修改

  先在线段树中向下找到包含pos的所有节点,将这些节点对应的Splay中原值删去, 加入新值即可

 4.求前驱

  同样找到区间内的所有Splay,分别进入找前驱,输出最大的前驱即可。但k可能不在Splay中,于是我们先要找到大于等于k的权值最小的节点,将它旋转到根,再找前驱,但由于可能没有前驱,就在每棵Splay插入-inf, 查询排名时记得减去即可

 5.求后继

  操作与前驱类似,找到小于等于k的权值最大的节点,旋转到根,找后继,记得每棵Splay插入inf就行

  大部分操作都是自己YY的,可能不是很优秀,但我这个又臭又长的代码并没有调试多久,也是不容易啊。

 代码(340行):

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100005, inf = 2147483647;template<class T> void read(T &re)
{re=0;T sign=1;char tmp;while((tmp=getchar())&&(tmp<'0'||tmp>'9')) if(tmp=='-') sign=-1;re=tmp-'0';while((tmp=getchar())&&(tmp>='0'&&tmp<='9')) re=(re<<3)+(re<<1)+(tmp-'0');re*=sign;
}int n, m, root[maxn<<1], rt, a[maxn];
int tot, cnt, lson[maxn<<1], rson[maxn<<1], stak[maxn], top, s[maxn], snum;struct Splay_tree{int fa, s[2], val, siz, num;
}tr[maxn * 20];void update(int x)
{int ls = tr[x].s[0], rs = tr[x].s[1];tr[x].siz = tr[ls].siz + tr[rs].siz + tr[x].num;
}void Rotate(int x)
{int y = tr[x].fa, z = tr[y].fa, k = (tr[y].s[1] == x), w = (tr[z].s[1] == y), son = tr[x].s[k^1];tr[y].s[k] = son;tr[son].fa = y;tr[x].s[k^1] = y;tr[y].fa = x;tr[z].s[w] = x;tr[x].fa = z;update(y);update(x);
}void Splay(int x, int to, int id)
{int y, z;while(tr[x].fa != to){y = tr[x].fa;z = tr[y].fa;if(z != to)Rotate((tr[y].s[0] == x) ^ (tr[z].s[0] == y)? x: y);Rotate(x);}if(!to)root[id] = x;
}void Insert(int x, int v)
{int now = root[x], ff = 0;while(now){ff = now;tr[now].siz ++;if(tr[now].val == v)    break;now = tr[now].s[v>tr[now].val];}if(now)tr[now].num ++;else{if(snum)now = s[snum--];elsenow = ++cnt;tr[now].val = v;tr[ff].s[v>tr[ff].val] = now;tr[now].fa = ff;tr[now].num = 1;tr[now].siz = 1;tr[now].s[0] = tr[now].s[1] = 0;}Splay(now, 0, x);
}void build(int &x, int l, int r)
{x = ++tot;root[x] = ++cnt;tr[root[x]].val = -inf;tr[root[x]].siz = tr[root[x]].num = 1;tr[0].s[1] = root[x];Insert(x, inf);for(int i = l; i <= r; ++i)Insert(x, a[i]);if(l == r)    return ;int mid = (l + r)>>1;build(lson[x], l, mid);build(rson[x], mid + 1, r);
}void Extract(int x, int l, int r, int L, int R)
{if(l <= L && R <= r){stak[++top] = x;return ;}int mid = (L + R)>>1;if(l <= mid)Extract(lson[x], l, r, L, mid);if(mid < r)Extract(rson[x], l, r, mid + 1, R);
}int Queryrk(int id, int x)
{int now = root[id], ret = 0;while(now){int ls = tr[now].s[0], rs = tr[now].s[1];if(x < tr[now].val){if(ls)now = ls;elsebreak;}    else if(x == tr[now].val){ret += tr[ls].siz ;break;}else{ret += tr[ls].siz + tr[now].num;if(rs)now = rs;elsebreak;}}tr[0].s[1] = root[id];Splay(now, 0, id);return ret;
}int work1(int x)
{int ret = -top;while(top){ret += Queryrk(stak[top], x);top --;}return ret + 1;
}int check(int x)
{int ret = 0;for(int i = 1; i <= top; ++i)ret += Queryrk(stak[i], x);return ret + 1;
}int work2(int x)
{x += top;int l = 0, r = 1e8, mid, ret = 0;while(l <= r){mid = (l + r)>>1;if(check(mid) <= x)ret = mid, l = mid + 1;elser = mid - 1;}top = 0;return ret;
}int Find(int now, int x)
{while(1){int ls = tr[now].s[0], rs = tr[now].s[1];if(tr[now].val == x)    return now;if(x < tr[now].val)    now = ls;else    now = rs;}
}int Querypre(int now)
{now = tr[now].s[0];while(tr[now].s[1])    now = tr[now].s[1];return now;
}int Querynxt(int now)
{now = tr[now].s[1];while(tr[now].s[0])    now = tr[now].s[0];return now;
}void Modify(int x, int pos, int l, int r, int v)
{tr[0].s[1] = root[x];int y = Find(root[x], a[pos]);Splay(y, 0, x);int pre = Querypre(root[x]);int nxt = Querynxt(root[x]);Splay(pre, 0, x);Splay(nxt, pre, x);if(tr[y].num > 1){tr[y].num --;tr[y].siz --;}else{s[++snum] = y;tr[nxt].s[0] = 0;}update(nxt);update(pre);Insert(x, v);if(l == r){a[pos] = v;return ;}int mid = (l + r)>>1;if(pos <= mid)Modify(lson[x], pos, l, mid, v);elseModify(rson[x], pos, mid + 1, r, v);
}int Findmx(int now, int x)
{int ret = 0;while(now){int ls = tr[now].s[0], rs = tr[now].s[1];if(tr[now].val == x)return now;if(tr[now].val > x){ret = now;now = ls;}elsenow = rs;}return ret;
}int Findmn(int now, int x)
{int ret = 0;while(now){int ls = tr[now].s[0], rs = tr[now].s[1];if(tr[now].val == x)return now;if(tr[now].val < x){ret = now;now = rs;}elsenow = ls;}return ret;
}int work3(int x)
{int ret = -inf;while(top){tr[0].s[1] = root[stak[top]];int now = Findmx(root[stak[top]], x);Splay(now, 0, stak[top]);int pre = Querypre(root[stak[top]]);ret = max(ret, tr[pre].val);top--;}return ret;
}int work4(int x)
{int ret = inf;while(top){tr[0].s[1] = root[stak[top]];int now = Findmn(root[stak[top]], x);Splay(now, 0, stak[top]);int nxt = Querynxt(root[stak[top]]);ret = min(ret, tr[nxt].val);top--;}return ret;
}int main()
{read(n);read(m);for(int i = 1; i <= n; ++i)read(a[i]);build(rt, 1, n);for(int i = 1; i <= m; ++i){int opt, l, r, k, pos;read(opt);if(opt == 1){read(l);read(r);read(k);Extract(rt, l, r, 1, n);printf("%d\n", work1(k));}else if(opt == 2){read(l);read(r);read(k);Extract(rt, l, r, 1, n);printf("%d\n", work2(k));}else if(opt == 3){read(pos);read(k);Modify(rt, pos, 1, n, k);}else if(opt == 4){read(l);read(r);read(k);Extract(rt, l, r, 1, n);printf("%d\n", work3(k));}else{read(l);read(r);read(k);Extract(rt, l, r, 1, n);printf("%d\n", work4(k));}}return 0;
}

View Code

转载于:https://www.cnblogs.com/Joker-Yza/p/11243378.html

二逼平衡树——树套树(线段树套Splay平衡树)相关推荐

  1. BZOJ 3218(a + b Problem-二分图套值域线段树)

    出这题的人是怎么想出来的-- 言归正传,这题是二分图套值域线段树. 首先经过 @Vfleaking的神奇建图后,把图拆成二分图, 不妨利用有向图最小割的性质建图(以前我一直以为最小割和边的方向无关,可 ...

  2. P5787 二分图 /【模板】线段树分治(线段树分治、并查集)

    关于什么是合理的实现 解析 本题把并查集写在了题面上 然而,我却一直沉浸在一个及其通用的判断二分图的方法中: 一个图是二分图的充要条件是它没有奇环 怎么维护这个玩意?带权并查集! 怎么套线段树分治?可 ...

  3. 势能线段树/吉司机线段树-我没有脑子

    势能线段树/吉司机线段树 BZOJ3211 花神游历各国 BZOJ5312 冒险 BZOJ4355 Play with sequence BZOJ4695 最假女选手 \(A_i = max(A_i, ...

  4. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 1153  Solved: 421 [Submit][Sta ...

  5. BZOJ3862Little Devil I——树链剖分+线段树

    题目大意: 给一棵树,每条边可能是黑色或白色(起始都是白色),有三种操作: 1.将u到v路径上所有边颜色翻转(黑->白,白->黑) 2.将只有一个点在u到v路径上的边颜色翻转 3.查询u到 ...

  6. CodeForces - 160D Edges in MST(思维+tarjan/树链剖分+线段树)

    题目链接:点击查看 题目大意:给出一张 n 个点 m 条边组成的带权无向图,现在对于每条边来说,确定一下其分类: 一定是最小生成树上的边 可能是最小生成树上的边 一定不是最小生成树的边 题目分析:两种 ...

  7. CodeForces - 609E Minimum spanning tree for each edge(最小生成树+树链剖分+线段树/树上倍增)

    题目链接:点击查看 题目大意:给出一张 n 个点和 m 条边组成的无向图,现在询问包含每一条边的最小生成树 题目分析:考虑求解次小生成树的思路: 求出最小生成树 ans 枚举每一条非树边 ( u , ...

  8. 主席树——多棵线段树的集合

    主席树: (不要管名字) 我们有的时候,会遇到很多种情况,对于每一种情况,都需要通过线段树的操作实现. 碰巧的是,相邻两种情况下的线段树的差异不大.(总体的差异次数是O(N)级别的,均摊就是O(常数) ...

  9. P2486 [SDOI2011]染色(树链剖分+线段树)

    题干描述 输入描述 输出格式 对于每个询问操作,输出一行答案. 输入输出样例 输入 #1 复制 6 5 2 2 1 2 1 1 1 2 1 3 2 4 2 5 2 6 Q 3 5 C 2 1 1 Q ...

  10. D-query SPOJ - DQUERY(求区间不同数的个数)(树状数组||线段树+离散)(主席树+在线)

    English Vietnamese Given a sequence of n numbers a1, a2, -, an and a number of d-queries. A d-query ...

最新文章

  1. DNN安装报错-The stored procedure 'dbo.GetPortalAliasByPortalID' doesn't exist.如何解决
  2. Android Studio 第四十九期 - Sqlite数据库四种写法
  3. 德哥的PostgreSQL私房菜 - 史上最屌PG资料合集
  4. 在64位系统上注册并使用32位的COM组件
  5. feign返回结果统一处理_在.net core中对接口返回属性名称做统一处理
  6. ❤️不一样的测试之旅:医疗行业软件测试有什么不一样?❤️
  7. 【公开课】斯坦福2019秋季课程:图机器学习资料全公开
  8. 数据--第44课 - 最小连通网
  9. yii 学习笔记五、通过gii创建modules(模块)
  10. 深度优先搜索 详解(C++)
  11. 大数据技术笔记之数据采集和预处理
  12. Cocos Creator 虚拟摇杆
  13. 手势解锁java后端设计_Android进阶 - 手势解锁
  14. w8ndows 秒表,关闭 Windows Search,Win8 能变快?
  15. vue中使用layui实现树形菜单增删改查功能
  16. docker安装包安装
  17. 单片机计数器实验代码c语言,单片机计数器功能实验程序
  18. Java Spring Cloud XII 之 单点登录
  19. jupyter内核无法连接,出现error,代码无法运行解决办法
  20. 【观察】解读神州鲲泰:二十年磨一剑,积硅步至千里

热门文章

  1. 禅道Docker安装包发布
  2. Idea SpringBoot 基于 Docker容器环境进行远程调试
  3. SpringBoot2.3 修改响应头、添加更新token、解决在过滤器中修改失败
  4. 不要为了面子伤了自己
  5. 命名实体识别NER遗留问题----模型构建
  6. 本机无法访问虚拟机上的nignx
  7. PVD与CVD性能比较
  8. 2021年大数据HBase(十七):❤️HBase的360度全面调优❤️
  9. 2021年大数据常用语言Scala(三十一):scala面向对象 特质(trait)
  10. 山西农业大学c语言答案,第一章C语言及程序设计概述-东北农业大学教务处.doc...