书架

题目

传送门 to vjudge

题目描述
T T T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用 1 1 1 n n n的正整数给每本书都编了号。 小 T T T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小 T T T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有 X X X本书,那么放回去时这本书上面就只可能有 X − 1 X-1 X1 X X X X + 1 X+1 X+1本书。 当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小 T T T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。 久而久之,小 T T T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:

  1. 编号为 X X X的书在书柜的什么位置;
  2. 从上到下第 X X X本书的编号是多少。

答案对 N + 1 N+1 N+1取模。——好吧原题目没有这句话,我只是想把它改得面目全非而已。

输入输出格式
输入格式:
第一行有两个数 n , m n,m n,m,分别表示书的个数以及命令的条数。

第二行为 n n n个正整数:第 i i i个数表示初始时从上至下第 i i i个位置放置的书的编号。

第三行到 m + 2 m+2 m+2行,每行一条命令。命令有 5 5 5种形式:

  1. Top S \text{Top S} Top S——表示把编号为 S S S的书放在最上面。
  2. Bottom S \text{Bottom S} Bottom S——表示把编号为 S S S的书放在最下面。
  3. Insert S T \text{Insert S T} Insert S T—— T ∈ { − 1 , 0 , 1 } T\in\{-1,0,1\} T{1,0,1},若编号为 S S S的书上面有 X X X本书,则这条命令表示把这本书放回去后它的上面有 X + T X+T X+T本书。
  4. Ask S \text{Ask S} Ask S——询问编号为 S S S的书的上面目前有多少本书。
  5. Query S \text{Query S} Query S——询问从上面数起的第 S S S本书的编号。

输出格式:
对于每一条 Ask \text{Ask} Ask Query \text{Query} Query语句你应该输出一行,一个数,代表询问的答案。

数据范围与约定
100 % 100\% 100%的数据, n , m ≤ 80000 n,m\le 80000 n,m80000

思路

考虑用平衡树解决这个问题。

我们的目标是找到一种 key \text{key} key值的定义,使得这样一条性质成立:如果书 x x x key \text{key} key小于书 y y y key \text{key} key,那么书架中 x x x y y y的上面。

更通俗一点,让平衡树的中序遍历恰好为书架中的书顺序。

发现这是可以做的。存储一个 L , R \mathcal{L,R} L,R表示当前已经使用的 key \text{key} key中的最小值和最大值。

放到顶端,相当于 key x = L − 1 \text{key}_x=\mathcal L-1 keyx=L1;放到底部,相当于 key x = R + 1 \text{key}_x=\mathcal R+1 keyx=R+1。记得更新 L \mathcal L L R \mathcal R R

与旁边的某本书交换位置,直接交换 key \text{key} key即可。

更改 key \text{key} key的时候,把原来的删掉,然后插入新的。

然后 Ask \text{Ask} Ask Query \text{Query} Query就是排名查询和查询第 k k k个元素。多板啊!

代码

我写的带旋 Treap \text{Treap} Treap。而且用了类模板。拷贝下来,就是整个世界

#include <cstdio>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
inline int readint(){
	int a = 0, f = 1; char c = getchar();
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -1;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar(x%10+'0');
}

template < typename T >
class Treap{
	struct Node{
		T data; int cnt, size, prio;
		Node *son[2], *fa;
		Node(const T &__data):data(__data){
			prio = rand(), cnt = size = 1;
			son[0] = son[1] = fa = NULL;
		}
		bool operator<(const Node &that)const{
			return prio < that.prio;
		}
		void maintain(){
			size = cnt;
			if(son[0] != NULL)
				size += son[0]->size;
			if(son[1] != NULL)
				size += son[1]->size;
		}
	};
	Node* root;
	void rotate(Node* &o,int d){
		Node* k = o->son[d];
		o->son[d] = k->son[d^1];
		if(o->son[d] != NULL)
			o->son[d]->fa = o;
		k->son[d^1] = o;
		k->fa = o->fa; o->fa = k;
		o->maintain(), k->maintain();
		o = k; // change root 
	}
	void insert(T __data,Node* &o){
		if(o == NULL){
			o = new Node(__data);
			return ;
		}
		if(o->data == __data){
			++ o->cnt; ++ o->size;
			return ;
		}
		int d = o->data < __data;
		insert(__data,o->son[d]);
		o->son[d]->fa = o;
		if(*o < *(o->son[d]))
			rotate(o,d);
		o->maintain();
	}
	void erase(T __data,Node* &o){
		if(o == NULL)
			return ; // err to erase 
		int d;
		if(o->data == __data){
			if(o->cnt > 1){
				-- o->cnt; -- o->size;
				return ;
			}
			if(o->son[0] == NULL and o->son[1] == NULL){
				delete o; o = NULL; return ;
			}
			if(o->son[0] == NULL or o->son[1] == NULL)
				d = (o->son[1] != NULL);
			else d = (*(o->son[0]) < *(o->son[1]));
			rotate(o,d);
			erase(__data,o->son[d^1]);
		}
		else{
			d = o->data < __data;
			erase(__data,o->son[d]);
		}
		o->maintain();
	}
	unsigned getRank(T __data,Node* o){
		if(o == NULL)
			return 0; // nothing more 
		unsigned lsize = (o->son[0] == NULL ? 0 : o->son[0]->size);
		if(o->data == __data)
			return 1+lsize;
		int d = o->data < __data;
		return d*(lsize+o->cnt)+getRank(__data,o->son[d]);
	}
	T kthElement(unsigned k,Node* o){
		unsigned lsize = (o->son[0] == NULL ? 0 : o->son[0]->size);
		if(k > lsize){
			k -= lsize, k -= o->cnt;
			if(k <= 0) return o->data;
			return kthElement(k,o->son[1]);
		}
		return kthElement(k,o->son[0]);
	}
	void dispose(Node* &o){
		if(o == NULL)
			return ;
		dispose(o->son[0]);
		dispose(o->son[1]);
		delete o; o = NULL;
	}
public:
	void dispose(){
		dispose(root);
	}
	void clear(){
		root = NULL;
	}
	Treap(){ clear(); }
	void insert(T __data){
		insert(__data,root);
	}
	void erase(T __data){
		erase(__data,root);
	}
	unsigned getRank(T __data){
		return getRank(__data,root);
	}
	T kthElement(unsigned k){
		if(root != NULL and k){
			unsigned all = root->size;
			if(k > all) k = all;
			return kthElement(k,root);
		}
	}
};

struct ZXY{
	int val, id;
	ZXY(int V,int I):val(V),id(I){}
	bool operator<(const ZXY &that)const{
		return val < that.val;
	}
	bool operator==(const ZXY &that)const{
		return val == that.val;
	}
};

const int MaxN = 80000;
int ppl[MaxN+1], n, m, lid, rid;
Treap<ZXY> meimei;

void input(){
	srand(5201314);
	n = readint(), m = readint();
	lid = 1, rid = n;
	for(int i=1; i<=n; ++i)
		ppl[readint()] = i;
	for(int i=1; i<=n; ++i)
		meimei.insert(ZXY(ppl[i],i));
}

char cmd[1478];
void solve(){
	int one, two;
	while(m --){
		scanf("%s",cmd);
		one = readint();
		if(cmd[0] == 'T'){
			meimei.erase(ZXY(ppl[one],one));
			ppl[one] = -- lid;
			meimei.insert(ZXY(ppl[one],one));
		}
		if(cmd[0] == 'B'){
			meimei.erase(ZXY(ppl[one],one));
			ppl[one] = ++ rid;
			meimei.insert(ZXY(ppl[one],one));
		}
		if(cmd[0] == 'I'){
			two = readint();
			if(not two) continue;
			int located = meimei.getRank(ZXY(ppl[one],one));
			two = meimei.kthElement(located+two).id;
			meimei.erase(ZXY(ppl[one],one));
			meimei.erase(ZXY(ppl[two],two));
			swap(ppl[one],ppl[two]);
			meimei.insert(ZXY(ppl[one],one));
			meimei.insert(ZXY(ppl[two],two));
		}
		if(cmd[0] == 'A'){
			writeint(meimei.getRank(ZXY(ppl[one],one))-1);
			putchar('\n');
		}
		if(cmd[0] == 'Q'){
			writeint(meimei.kthElement(one).id);
			putchar('\n');
		}
	}
}

int main(){
	input(), solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文档管理精灵-电子书架 功能简介 相信朋友你的各种各样的文档资料也不少吧,面对一大堆的资料,如何去管理它?如何很快地从自己的一大堆资料中找出需要的主题内容?不用愁,有了电子书架,一切的烦恼都会离你而去。言归正传,就让我们来看看她是一个什么样的一个东东吧。 适合对象 电子书架用于文档管理,所以适合于以下人员:网上";;;拾贝者";;;截取保存网上信息、文秘工作者管理重要档案、新闻媒体工作者保存面信息、能进行计算机操作的学生作笔记、教师备课、各种科技人员撰写保存论文、游戏攻略收集者、炒股人员保存股市信息...等等...以及各种需要进行文档管理的信息工作者;但由于电子书架的特殊功用,所以又适合于以下人员:程序员、网站站长管理制作网页、机密档案管理员。 功能特点 电子书架的功能特点是:快速的资料截取、迅捷的资料查找、清晰的资料管理、丰富的格式转换、方便的电子制作、安全的资料保存、多用户支持、Delphi及VB代码文本格式化等等,具体功能特点如下: 资料即取即存 电子书架为您收集资料预备了一个很实用的功能,就是";;;即取即存";;;,您所要做的仅仅是";;;复制";;;,操作就是这么简单。您只要开启电子书架,点一下菜单[工具]->[监视剪贴板],或者就按F7,好了,你在任何地方复制或剪切的资料都会被电子书架所收集并保存,并智能建立标题,不管你复制的是纯文本、漂亮的RTF格式文本、还是HTML网页,电子书架都为你忠实地保存下来。当然你也可以在这几种格式之间转化切换。你只要看到什么有用就复制一下,电子书架会自动为你保存,不用麻烦您高贵的手再去粘贴,避免了Windows常规操作时又复制又粘贴地频繁在各窗口间切换的烦琐,非常方便于您截取有价值的资料。你如果把电子书架最小化了(可以隐藏于系统栏),那么就更酷了,打个比方,电子书架就象是你办公室灵巧的小秘,你只要复制你所要的,她就会把你的内容接管过去保存起来。 资料迅速查找 可以查标题、可以查内容;可以精确查找、可以模糊查找;可以逐个查找、可以批量查找;可以在当前目录位置查找、也可以在全库中搜索。与此同时,为了更方便迅速地找到需要的文档,电子书架为你设置了20个签,使您能在常要查找的目录之间迅速跳转; 资料交流 电子书架内置了Email功能,您可以随时将资料库中的内容E-Mail给您的朋友,也可以带上附件;设有好友通讯录; 网址收藏 无论您在哪复制一下网址,Collector就会问你要不要保存这个网址,并保存为HTML格式,建立链接。当然,您如果认为这个讨厌,您也可以关闭这个功能(只要不监视就可以了); IE安全恢复 在网上取一些资料时,鉴于有个别网站为了打响自己,私自修改用户Internet Explorer默认页及标题设置,并锁定注册表、禁用默认页修改,开机自动进入其网站。在电子书架中增加了IE安全恢复的功能,如果您的Internet Explorer已经被修改,可以用恢复功能恢复到默认设置(其实是三脚猫功夫,只不过是修改注册表而已); 多格式支持 电子书架支持的TXT、RTF、HTML等多种格式,所以,可以根据个人需要变化多样,非常漂亮。参见:《附录》-《电子书架范本》中的示例效果; 多格式间转换 可以在电子书架支持的TXT、RTF、HTML等格式之间编辑及相互转化,具备批量文件格式转化功能,您可以批量转换磁盘上的文件,可作为HTML自动生成、文本格式转换、小型Internet浏览工具;同时您也可以调用外部的任何一个文本编辑器(如WordPad、Microsoft Word)、网页编辑程序来对文档或者网页进行编辑,调用Microsoft Word方便您插入各种字符和表格(表格插入功能不完善);RTF-HTML转换功能,可以把存在于电子书架中的资料导出为HTML文件,导出后格式不会丢失; 编码转换 支持在BIG5和GB之间、DOS、UNIX之间相互转化,也可作为一个码制转换工具使用; 资料导入 支持鼠标拖放导入操作,您可以把文件或者文件夹拖入到电子书架内,文件或者文件夹的内容就会按照您的设置被导入,导入时智能判断BIG、HTML格式; 资料导出、电子制作、批量网页制作 您存放在电子书架内的资料可以方便地导出为TXT纯文本文件、RTF多格式文档、HTML网页文件、Windows帮助格式的CHM文件、甚至CHM工程文件组。可以批量导出,导出生成的HTML文件自动建立索引页。这个功能使得电子书架将来有可能成为电子制作、批量网页制作、批量文件生成的一个好工具; 光碟目录管理 这一个功能实在简陋,没有什么特色。功能是
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值