[AHOI2017/HNOI2017]单旋
题目
点这里看题目。
分析
最妙的地方在于,这道题其实是用一种数据结构模拟另一种数据结构!
我们需要维护深度和树的结构,以下对于每个操作进行分别讨论。
插入一个新节点
可以发现,这个新节点一定会成为自己的前驱或者后继中深度较大者的儿子。
然后可以更新深度和树的结构。
单旋最小值
发现树会有如下的变化:
如果自己有儿子,那么它只会是自己的右儿子;旋转后,它会接到自己原先的父亲上。
除了自己的儿子以外,其它的节点的深度都会 +1 。
原先的根变成了最小值的儿子,根随后变成最小值。
单旋最大值同理分析。
单旋最小值并删除
先模拟好单旋的修改,然后考虑旋转后的变化:
所有节点深度 -1 。
断开最小值和它的右儿子的边,根变为它的右儿子。
单旋最大值并删除同理分析。
发现树的深度的修改实际上可以理解为值域上一段区间的加减,因此可以用权值线段树维护;前驱后继也可以顺便用它维护了。
中途我们需要用 map 来维护树的形态,或者离散化之后直接存下来也可以。
时间复杂度:\(O(n\log_2|V|)\)。
代码
#include <map>
#include <cstdio>
using namespace std;
const int up = 1e9/*4*/;
const int MAXN = 1e5 + 5, MAXLOG = 30, MAXS = MAXN * MAXLOG/*205*/;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
map<int, int> fa, ch[2];
int siz[MAXS], dep[MAXS], tag[MAXS], lch[MAXS], rch[MAXS];
int tot, rt, sgrt;
void upt( const int x ) { siz[x] = siz[lch[x]] + siz[rch[x]]; }
void add( const int x, const int v ) { dep[x] += siz[x] * v, tag[x] += v; }
void normalize( const int x ) { if( tag[x] ) add( lch[x], tag[x] ), add( rch[x], tag[x] ), tag[x] = 0; }
void setNode( int &u, const int l, const int r, const int pos, const int v )
{
if( ! u ) u = ++ tot;
if( l == r ) { siz[u] += v; return ; }
int mid = l + r >> 1; normalize( u );
if( pos <= mid ) setNode( lch[u], l, mid, pos, v );
else setNode( rch[u], mid + 1, r, pos, v );
upt( u );
}
void update( int &u, const int l, const int r, const int segL, const int segR, const int v )
{
if( ! u ) u = ++ tot;
if( segL <= l && r <= segR ) { add( u, v ); return ; }
int mid = l + r >> 1; normalize( u );
if( segL <= mid ) update( lch[u], l, mid, segL, segR, v );
if( mid < segR ) update( rch[u], mid + 1, r, segL, segR, v );
upt( u );
}
int query( const int u, const int l, const int r, const int segL, const int segR )
{
if( ! u ) return 0;
if( segL <= l && r <= segR ) return siz[u];
int mid = l + r >> 1, ret = 0; normalize( u );
if( segL <= mid ) ret += query( lch[u], l, mid, segL, segR );
if( mid < segR ) ret += query( rch[u], mid + 1, r, segL, segR );
return ret;
}
int Kth( const int u, const int l, const int r, const int rnk )
{
if( l == r ) return l;
int mid = l + r >> 1; normalize( u );
if( rnk <= siz[lch[u]] ) return Kth( lch[u], l, mid, rnk );
return Kth( rch[u], mid + 1, r, rnk - siz[lch[u]] );
}
int getDep( const int u, const int l, const int r, const int pos )
{
if( l == r ) return dep[u];
int mid = l + r >> 1; normalize( u );
if( pos <= mid ) return getDep( lch[u], l, mid, pos );
return getDep( rch[u], mid + 1, r, pos );
}
int Kth( const int rnk ) { if( rnk <= 0 ) return -1; if( rnk > siz[sgrt] ) return up + 1; return Kth( sgrt, 1, up, rnk ); }
int getDep( const int id ) { if( id < 0 || id > up ) return -0x3f3f3f3f; return getDep( sgrt, 1, up, id ); }
int getMx( const int u, const int l, const int r )
{
if( l == r ) return l;
int mid = l + r >> 1; normalize( u );
if( siz[rch[u]] ) return getMx( rch[u], mid + 1, r );
return getMx( lch[u], l, mid );
}
int getMn( const int u, const int l, const int r )
{
if( l == r ) return l;
int mid = l + r >> 1; normalize( u );
if( siz[lch[u]] ) return getMn( lch[u], l, mid );
return getMn( rch[u], mid + 1, r );
}
int rotMn()
{
int id = getMn( sgrt, 1, up ), rig = ch[1][id], y = fa[id];
if( rt == id ) return 1;
if( rig ) fa[rig] = y; fa[rt] = id, fa[id] = 0;
ch[1][id] = rt, ch[0][y] = rig, rt = id;
int ret = getDep( id );
update( sgrt, 1, up, y, up, 1 );
update( sgrt, 1, up, id, id, -ret + 1 );
return ret;
}
int rotMx()
{
int id = getMx( sgrt, 1, up ), lef = ch[0][id], y = fa[id];
if( rt == id ) return 1;
if( lef ) fa[lef] = y; fa[rt] = id, fa[id] = 0;
ch[0][id] = rt, ch[1][y] = lef, rt = id;
int ret = getDep( id );
update( sgrt, 1, up, 1, y, 1 );
update( sgrt, 1, up, id, id, -ret + 1 );
return ret;
}
int main()
{
int op, k, N;
read( N );
while( N -- )
{
read( op );
if( op == 1 )
{
read( k ), setNode( sgrt, 1, up, k, 1 );
if( siz[1] == 1 )
{
fa[rt = k] = 0, update( sgrt, 1, up, k, k, 1 );
puts( "1" );
continue;
}
int rnk = query( sgrt, 1, up, 1, k );
int pre = Kth( rnk - 1 ), suf = Kth( rnk + 1 );
int dpre = getDep( pre ), dsuf = getDep( suf );
if( dpre > dsuf ) fa[k] = pre, ch[1][pre] = k, update( sgrt, 1, up, k, k, dpre + 1 );
else fa[k] = suf, ch[0][suf] = k, update( sgrt, 1, up, k, k, dsuf + 1 );
write( getDep( k ) ), putchar( '\n' );
}
if( op == 2 ) write( rotMn() ), putchar( '\n' );
if( op == 3 ) write( rotMx() ), putchar( '\n' );
if( op == 4 )
{
int ans = rotMn(), tmp = rt, y = ch[1][tmp];
update( sgrt, 1, up, tmp + 1, up, -1 );
update( sgrt, 1, up, tmp, tmp, - ans );
if( y ) fa[y] = 0; ch[1][tmp] = 0, rt = y;
setNode( sgrt, 1, up, tmp, -1 );
write( ans ), putchar( '\n' );
}
if( op == 5 )
{
int ans = rotMx(), tmp = rt, y = ch[0][tmp];
update( sgrt, 1, up, 1, tmp - 1, -1 );
update( sgrt, 1, up, tmp, tmp, -ans );
if( y ) fa[y] = 0; ch[0][tmp] = 0, rt = y;
setNode( sgrt, 1, up, tmp, -1 );
write( ans ), putchar( '\n' );
}
}
return 0;
}
[AHOI2017/HNOI2017]单旋的更多相关文章
- bzoj 4825: [Hnoi2017]单旋 [lct]
4825: [Hnoi2017]单旋 题意:有趣的spaly hnoi2017刚出来我就去做,当时这题作死用了ett,调了5节课没做出来然后发现好像直接用lct就行了然后弃掉了... md用lct不知 ...
- 【LG3721】[HNOI2017]单旋
[LG3721][HNOI2017]单旋 题面 洛谷 题解 20pts 直接模拟\(spaly\)的过程即可. 100pts 可以发现单旋最大.最小值到根,手玩是有显然规律的,发现只需要几次\(lin ...
- 4825: [Hnoi2017]单旋
4825: [Hnoi2017]单旋 链接 分析: 以后采取更保险的方式写代码!!!81行本来以为不特判也可以,然后就总是比答案大1,甚至出现负数,调啊调啊调啊调~~~ 只会旋转最大值和最小值,以最小 ...
- [BZOJ4825][HNOI2017]单旋(线段树+Splay)
4825: [Hnoi2017]单旋 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 667 Solved: 342[Submit][Status][ ...
- 【BZOJ4825】[Hnoi2017]单旋 线段树+set
[BZOJ4825][Hnoi2017]单旋 Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能 ...
- bzoj4825 [Hnoi2017]单旋
Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...
- BZOJ:4825: [Hnoi2017]单旋
Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...
- HNOI2017 单旋
题目描述 网址:https://www.luogu.org/problemnew/show/3721 大意: 有一颗单旋Splay(Spaly),以key值为优先度,总共有5个操作. [1] 插入一个 ...
- HNOI2017单旋
单旋 这道题做法贼多,LCT,splay,线段树什么的貌似都行. 像我这种渣渣只会线段树了(高级数据结构学了也不会用). 首先离线所有操作,因为不会有两个点值重复,所以直接离散. 一颗线段树来维护所有 ...
随机推荐
- Verilog代码和FPGA硬件的映射关系(一)
代码和硬件之间的映射关系是一个很奇妙的过程,也展现出人类的智慧.单片机内部的硬件结构都是固定的,无法改变,我们通过代码操作着寄存器的读写,来执行各种复杂的任务.FPGA的硬件结构并不像单片机一样是固定 ...
- web自动化之键盘操作
简单介绍下web自动化怎么触发键盘操作 按键操作 需要导入的类from selenium.webdriver.common.keys import Keys 组合键Keys.CONTROL 也就是我们 ...
- [ES6系列-02]Arrow Function:Whats this?(箭头函数及它的this及其它)
[原创] 码路工人 大家好,这里是码路工人有力量,我是码路工人,你们是力量. 如果没用过CSharp的lambda 表达式,也没有了解过ES6,那第一眼看到这样代码什么感觉? /* eg.0 * fu ...
- android小Demo--七彩霓虹灯效果
七彩霓虹灯效果,基于网上的小Demo进行修改. 在android项目values文件夹下创建文件colors.xml,配置七种颜色: <?xml version="1.0" ...
- [JavaWeb基础] 018.Struts2 Action通配符使用
Struts2中有一个很牛逼的action通配符,可以用来简化action配置,以我们将要讲解的案例来说,如果我们要对一个学生信息进行增加,删除,修改,那么按照原来的做法,我们需要写3个Action来 ...
- while与do-while循环的使用
/* 1.格式: ①初始化条件 while(②循环条件){ ④循环体 ③迭代条件 } 2.for循环与while可以相互转换的 . */ public class TestWhile { //100以 ...
- PAT1067 试密码 (20分)——测试点4分析 一个易错点
1067 试密码 (20分) 当你试图登录某个系统却忘了密码时,系统一般只会允许你尝试有限多次,当超出允许次数时,账号就会被锁死.本题就请你实现这个小功能. 输入格式: 输入在第一行给出一个密码( ...
- Beta冲刺 —— 总结随笔
这个作业属于哪个课程 软件工程 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺 作业正文 正文 github链接 项目地址 其他参考文献 无 一.项目预期计划: 1.对于初版小程序的现 ...
- 数据库之 MySQL --- 数据处理 之 子查询 (二)
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一 .数据库语言定义及命令行查看数据库操作 -- SQL 语言可以分为三类-- DML: 数据操纵语言. ...
- Java实现 LeetCode 229 求众数 II(二)
229. 求众数 II 给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素. 说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1). 示例 1: 输入: [3,2, ...