首页 > 其他分享 >P2596 [ZJOI2006]书架 题解

P2596 [ZJOI2006]书架 题解

时间:2023-06-22 17:44:05浏览次数:55  
标签:左子 结点 ch int 题解 P2596 当前 ZJOI2006 节点

题目传送门:link

FHQ-Treap

解题的关键在于如何来求出一本书上面有多少本书,但考虑到我们里面没有像权值一样的东西来让我们用按值分裂来完成这个操作,所以考虑用按排名分裂来实现。

我们按照先后顺序把所有的书都给插入到平衡树里面,根据二叉搜索树的性质,当前结点的所有左子树的结点都是在当前结点代表的书的上面的书,但是其实并不只是当前结点的左子树,比如下面的图:

image

假设我们要查询 \(5\) 号点代表的书上面有几本书,我们就会发现,其实不止是 \(10\) 号点,还有 \(2\) 号结点的左子树所有点,实际上在平衡树中,这些点在原序列中都是在 \(5\) 号点代表的书的上方的。

我们能发现一个规律就是我们从当前的点开始算上当前结点的左子树大小,然后如果当前点是父节点的右儿子,那么我们跳到父节点,然后这时当前点的左子树的所有点都是在要查询的点代表的书的上方,所以我们累加当前点左子树的左子树大小加一,因为还有当前点也是在我们要查询的点代表的书的上方的。我们一直到了根节点才停止,这样就求出了当前点代表的书上方有几本书了。

用代码实现起来就是这样的:

inline int fid(int x)
{
	int xx = x, res = siz[ch[x][0]] + 1;
	while(xx != rt && x)
	{
		if(ch[fa[x]][1] == x) res += siz[ch[fa[x]][0]] + 1;
		x = fa[x];
	}
	return res;
}

题目中的各项操作都是基于给定书的上方有几本书来解决的,所以我们现在的主要问题就是父节点。

我们在分裂和合并的时候,要顺便维护一下当前点的父节点是谁,比如在分裂的过程中,我们多传两个参数表示当前点的候选父节点,也就是一个是左部分的当前分裂到的上一个结点和右部分分裂到的上一个结点,如果要是当前点的左子树的结点数小于 \(k\),我们就把当前点的父节点标记为上层传下来的左半部分的父节点,因为当前点是要被划分到左部分的,反之同理;对于合并操作,我们在递归往前推的时候记录一下更新完的点的父节点即可。

我们在一开始把所有书插入平衡树的时候是直接插到后面的,需要开一个数组来记录对应的书的编号在平衡树中结点的编号,在掉用上面的 fid 的时候直接用题目给的书的编号来查找当前编号的书的上面有多少本书即可。

对于 Insert 操作是 \(0\) 的就可以直接跳过,因为插入后还是在原来的位置。

code:

#include <bits/stdc++.h>

#define int long long
#define N 100100
#define endl '\n'

using namespace std;

int n, m, id[N], rt, cnt, ch[N][2], fa[N], siz[N], key[N], val[N];

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

inline void push_up(int x) {siz[x] = 1 + siz[ch[x][0]] + siz[ch[x][1]];}

inline int node(int x)
{
	cnt ++;
	val[cnt] = x;
	key[cnt] = rand();
	siz[cnt] = 1;
	id[x] = cnt;//记录当前值在平衡树中的结点中的编号
	return cnt ;
}

inline void split(int x, int k, int &xx, int &yy, int fx = 0, int fy = 0)
{
	if(x == 0) return xx = yy = 0, void();//下面要维护父节点的信息 
	if(siz[ch[x][0]] + 1 <= k) fa[x] = fx, xx = x, split(ch[x][1], k - siz[ch[x][0]] - 1, ch[x][1], yy, x, fy);
	else fa[x] = fy, yy = x, split(ch[x][0], k, xx, ch[x][0], fx, x);
	push_up(x);
}
 
inline int merge(int x, int y)
{
	if(! x || ! y) return x + y;
	if(key[x] < key[y])
	{
		ch[x][1] = merge(ch[x][1], y);
		fa[ch[x][1]] = x;//维护父节点的信息 
		push_up(x);
		return x;
	}
	else
	{
		ch[y][0] = merge(x, ch[y][0]);
		fa[ch[y][0]] = y; 
		push_up(y);
		return y;
	}
}

inline int fid(int x)//查找当前编号的书上面有多少本书 
{
	int xx = x, res = siz[ch[x][0]] + 1;
	while(xx != rt && x)
	{
		if(ch[fa[x]][1] == x) res += siz[ch[fa[x]][0]] + 1;
		x = fa[x];
	}
	return res;
}

signed main()
{
	srand((unsigned)time(NULL));
	string op;
	n = read(), m = read();
	for(int i = 1; i <= n; i ++)
	{
		int a = read();
		rt = merge(rt, node(a));
	}
	for(int i = 1; i <= m; i ++)
	{
		int x, y, z, z1, k, o;
		cin >> op;
		o = read();
//		if(op[0] != 'Q')
//			cout << "find:" << fid(id[o]) << endl;
		if(op[0] == 'T')
		{
			int xx = fid(id[o]);//找到上面有几本书之后按排名分裂 
			split(rt, xx, x, y);
			split(x, xx - 1, x, z);
			rt = merge(z, merge(x, y));//把当前编号的书放到顶端 
		}
		if(op[0] == 'B')
		{
			int xx = fid(id[o]);//同理把当前编号的书放到最下面 
			split(rt, xx, x, y, 0);
			split(x, xx - 1, x, z, 0);
			rt = merge(merge(x, y), z);
		}
		if(op[0] == 'I')
		{
			k = read();
			int xx = fid(id[o]);
			if(k == 0) continue;//放回原位就直接跳过 
			if(k == 1)//上面的书多了一本 
			{
				split(rt, xx + 1, x, y);//相当于和在平衡树中的后继互换位置 
				split(x, xx, x, z);
				split(x, xx - 1, x, z1);
				rt = merge(merge(merge(x, z), z1), y);
			}
			if(k == -1)//上面的书少了一本 
			{
				split(rt, xx, x, y);//相当于和在平衡树中的前驱互换位置 
				split(x, xx - 1, x, z);
				split(x, xx - 2, x, z1);
				rt = merge(merge(merge(x, z), z1), y);
			}
		}
		if(op[0] == 'A')
		{
			int xx = fid(id[o]);//查找上面有多少本书 
			cout << xx - 1 << endl;//因为里面的是包含自己的,所以要减去 
		}
		if(op[0] == 'Q')
		{
			split(rt, o, x, y);//按照o的排名分裂 
			int xx = x;
			while(ch[xx][1]) xx = ch[xx][1];//一直跳到最大值 
			cout << val[xx] << endl;
			rt = merge(x, y);
		}
	}
	return 0;
}

标签:左子,结点,ch,int,题解,P2596,当前,ZJOI2006,节点
From: https://www.cnblogs.com/Multitree/p/17498056.html

相关文章

  • Linux Nacos2.2.0版本集群搭建,常见报错问题解决
    准备:服务器,nacos,mysql,nginx,java,mavenNacos官网:https://nacos.io下载地址github:https://github.com/alibaba/nacos相关版本问题,见nacos官网手册查看集群配置图:官方的: 本次搭建集群配置图:开始搭建:修改nacos的配置文件“application.properties,cluster.conf.ex......
  • 金九银十首战告捷,五年Android开发工程师面试经验分享(附面试题解析)
    笔者从前期准备到所有面试结束,花费了差不多3个月的时间。真可谓“面试造火箭,工作拧螺丝”,面试过程真的很累很辛苦。笔者面了很多公司,最终拿下了百度、腾讯和京东的offer,最后可能会选择京东。有人可能会问为什么不选择腾讯?的确腾讯的工资很高,福利待遇也很好。我觉得在京东能接触到更......
  • CF248B Chilly Willy 题解
    CF248BChillyWilly解题过程经过简单思考,这道题肯定是由规律可循,因为\(n\le10^5\),只有高精度能存下。下面是暴力程序对\(n\)为\(1\)到\(13\)时的答案进行求解(\(11\)到\(13\)超出int范围了)。发现\(n\)为\(1\)或\(2\)时,他们的答案为\(-1\)。接着来分析......
  • UVA12222 Mountain Road 山路 题解 dp
    UVA12222山路题意:--一个山路只有一条车道,因此不能有两辆方向相反的车同时在车道内。同时,为了保证安全,车道内不能超车,且同向行驶的车间距必须大于10分钟。现在给你n辆车,三个参数依次表示行驶方向,到达时刻,行驶时间。问如何安排能使最后一个通过的车通过时的时刻最小,输出这个值......
  • 问题解决 --- surface go sd卡槽不识别问题
    问题描述之前好好的,突然发现没有识别sd卡,sd卡是好的问题原因可能是系统更新了uefi解决办法重启电脑,多次点按音量加键进入uefi,关闭sd卡,重启电脑到系统再次进入uefi,开启sd卡,重启电脑到系统,完成!......
  • 在一加7上kali nethunter安装好后更新到最新版本,vnc打开失败问题解决方法。
    首先说明nethunter的vnc本身就不稳定,是兼容性问题,而非非正常关闭导致的。解决方法:方法一:查看nethunre主app的开启vnc命令是不是终端不识别。现在vnc叫做kex。方法二:更新到最新版本,sudoaptupdate&aptupgrade,如果还是打不开的话,更新nethunre主app,在https://store.nethunter.co......
  • ARC162 题解
    A.EkidenRace(450)题意:\(n\)个人在进行往返跑比赛,其中第\(i\)个人在回程前的排名是\(i\),总排名是\(p_i\),问有多少个人可能成为回程中跑得最快的人?如果对于\(i\),存在某个\(j>i\),使得\(p_j<p_i\),那么\(j\)在回程途中超过了\(i\),\(i\)肯定不能成为答案,否则一定可以。......
  • zabbix设置中文后乱码问题解决
    zabbix设置中文后乱码问题解决 1.在本机控制面板找到字体选项(或者C:\Windows\Fonts文件夹选择一个上传到centos服务器中也可以)注意是复制,不是切题,因为windows系统自己还得要用字体。我这里选择的是简体黑体 2.服务器搜索zabbix的fonts目录 find/-namefonts cd......
  • [TJOI2007]路标设置 题解
    题目链接:https://www.luogu.com.cn/problem/P3853题目大意:给出一个递增数组,插入K个值,使其差分数列的最大值最小;值得注意的是,此题中每个数字都是整数考点:整数二分错误思路:利用堆排,取最大值直接二分code:1#include<bits/stdc++.h>23usingnamespacestd;45int......
  • UVA11090 Going in Cycle!!题解
    题目大意给定一个N个点M条边的带权有向图,求平均值最小的回路。解法看到这种题目,喜欢打暴力的我一下就想到:遍历整个图,找到每一个环,然后算出它们的平均值,最后比较出最小值。然而,呃...,会T飞...既然我们不能暴力找最小值,那还有什么别的办法吗?我们只需要输出一个最小值就行了,既然......