[NOIP2017D2T3]列队

虽然放在第三题但是其实这题没有什么思维难度,就是建$n$棵区间节点平衡树维护每行的前$m-1$个数,再建一棵平衡树维护最后一列

我用了splay,维护移动操作的确是挺简单的

但考前没有写过这种点代表区间而且要动态开点的题,怕写错不敢写,只写了60分

但实际上还是得敢写,既然自己肝了那么久splay确保自己不会写错那么为什么不试试新东西呢

其实也不是太困难,要找$x$,如果当前区间为$[l,r]$且$l\leq x\leq r$就把区间分成$[l,x-1],x,[x+1,r]$三部分就行了,没有太多细节

割分与合并记得更新父节点的信息就好

#include<stdio.h>
#define ll long long
struct node{
	ll l,r;
	int siz,fa,ch[2];
}t[1200010];
int root[300010],tot,n,m;
void pushup(int x){
	t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+(int)(t[x].r-t[x].l+1);
}
void rot(int x){
	int y,z,B,f;
	y=t[x].fa;
	z=t[y].fa;
	f=(t[y].ch[0]==x);
	B=t[x].ch[f];
	t[x].fa=z;
	t[y].fa=x;
	if(B)t[B].fa=y;
	t[x].ch[f]=y;
	t[y].ch[f^1]=B;
	if(t[z].ch[0]==y)t[z].ch[0]=x;
	if(t[z].ch[1]==y)t[z].ch[1]=x;
	pushup(y);
	pushup(x);
}
void splay(int x){
	int y,z;
	while(t[x].fa){
		y=t[x].fa;
		z=t[y].fa;
		if(z)rot((t[z].ch[0]==y&&t[y].ch[0]==x)||(t[z].ch[1]==y&&t[y].ch[1]==x)?y:x);
		rot(x);
	}
}
int getkth(int x,int k){
	if(t[t[x].ch[0]].siz<k&&k<=t[x].siz-t[t[x].ch[1]].siz){
		ll p=t[x].l+(ll)(k-t[t[x].ch[0]].siz-1);
		if(t[x].l<p){
			tot++;
			t[tot].l=t[x].l;
			t[tot].r=p-1;
			t[tot].fa=x;
			t[tot].ch[0]=t[x].ch[0];
			t[t[x].ch[0]].fa=tot;
			t[x].ch[0]=tot;
			t[x].l=p;
			pushup(tot);
		}
		if(p<t[x].r){
			tot++;
			t[tot].l=p+1;
			t[tot].r=t[x].r;
			t[tot].fa=x;
			t[tot].ch[1]=t[x].ch[1];
			t[t[x].ch[1]].fa=tot;
			t[x].ch[1]=tot;
			t[x].r=p;
			pushup(tot);
		}
		pushup(x);
		return x;
	}
	int f;
	if(k<=t[t[x].ch[0]].siz)f=getkth(t[x].ch[0],k);
	if(k>t[x].siz-t[t[x].ch[1]].siz)f=getkth(t[x].ch[1],k-t[x].siz+t[t[x].ch[1]].siz);
	pushup(x);
	return f;
}
void build(int l,int r,int&x){
	if(x==0){
		tot++;
		x=tot;
	}
	int mid=(l+r)>>1;
	t[x].l=t[x].r=m*(ll)mid;
	if(l<mid){
		build(l,mid-1,t[x].ch[0]);
		t[t[x].ch[0]].fa=x;
	}
	if(mid<r){
		build(mid+1,r,t[x].ch[1]);
		t[t[x].ch[1]].fa=x;
	}
	pushup(x);
}
int merge(int x,int y){
	if(x==0||y==0)return x|y;
	while(t[x].ch[1])x=t[x].ch[1];
	splay(x);
	t[x].ch[1]=y;
	t[y].fa=x;
	pushup(x);
	return x;
}
int split(int p){
	int k,j;
	k=t[p].ch[0];
	j=t[p].ch[1];
	t[p].ch[0]=t[p].ch[1]=0;
	t[k].fa=t[j].fa=0;
	pushup(p);
	return merge(k,j);
}
int main(){
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	int q,i,x,y,p,f;
	scanf("%d%d%d",&n,&m,&q);
	if(m>1){
		for(i=1;i<=n;i++){
			root[i]=i;
			t[i].l=m*(ll)(i-1)+1ll;
			t[i].r=m*(ll)(i-1)+(ll)(m-1);
			t[i].siz=m-1;
		}
		tot=n;
	}
	build(1,n,root[n+1]);
	while(q--){
		scanf("%d%d",&x,&y);
		if(y==m){
			p=getkth(root[n+1],x);
			splay(p);
			printf("%lld\n",t[p].l);
			root[n+1]=split(p);
			root[n+1]=merge(root[n+1],p);
		}else{
			p=getkth(root[x],y);
			splay(p);
			printf("%lld\n",t[p].l);
			f=getkth(root[n+1],x);
			splay(f);
			root[x]=split(p);
			root[n+1]=split(f);
			root[x]=merge(root[x],f);
			root[n+1]=merge(root[n+1],p);
		}
	}
}
时间: 2024-08-30 17:53:45

[NOIP2017D2T3]列队的相关文章

NOIP2017D2T3 列队—Treap

NOIP2017列队 Description Sylvia 是一个热爱学习的女孩子.  前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为m.  为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中的学生从 1 到 n × m 编上了号码(参见后面的样例).即:初始时,第 i 行第 j 列的学生的编号是(i−1)×m+j.  然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队.

树套树Day2

滚回来更新,,, 在Day1我们学了最基本的线段树套平衡树 Day2开始我们要学习一些黑科技 (所以很大概率会出现Day3 w 1.线段树上的黑科技 这一段我们分几项来讲 1.权值线段树 权值线段树以权值为下标建树(就像求逆序对时用的树状数组),一开始所有节点都为0,通过线段树的区间极值,区间和来表示"这个区间上有多少个数"等信息. 下面这个代码并没有离散化因为我懒得写↓ #include <iostream> #include <cstdio> using n

[LuoguP2161[ [SHOI2009]会场预约 (splay)

题面 传送门:https://www.luogu.org/problemnew/show/P2161 Solution splay 的确有线段树/树状数组的做法,但我做的时候脑残没想到 我们可以考虑写一个类似NOIP2017D2T3列队那道题那样的带分裂的平衡树 考虑用splay维护每一条线段的左端点和右端点 因为我们题目的意思保证了在平衡树里的线段不相交,所以我们可以考虑以下的性质 每一条线段作为一个点放入平衡树中,维护其L,R,并记录它是空白线段还是有预约的线段 我们要查询一段区间,设这个区

BZOJ2720: [Violet 5]列队春游

2720: [Violet 5]列队春游 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 173  Solved: 125[Submit][Status][Discuss] Description Input Output Sample Input Sample Output HINT 题解:对于这种题目我只能呵呵一笑欺负我是单身汪,哎! 一. 二.枚举每个位置,在枚举每个人,枚举每个人由当前位置可以看到的地方 于是: 于是我们如代码转移即可!p为当

循环列队的循序结构

</pre><pre name="code" class="cpp">//1.队列顺序结构的定义 #define MAXQSIZE 100 typedef struct { QElemType base[MAXQSIZE];//静态数组 int front;//队列头指针 int rear;//队列尾指针 }SqQueue; //解决队列的假溢出方法 //1.将循序列队臆造为一个环状空间.尾指针指向头指针 //2.在对满的情况下,rear指针

并发系统数据细节-列队

列队数据结构图形 stC代码实现,PHP程序需要懂C代码这是基础哈 #include<stdio.h> #include<stdlib.h> #include<memory.h> #define N 100 //定义常量N 为10 #define mytype int //定义常量mytype 替换int struct MyQueue { mytype data[N];//数组存储队列 int front;//拉屎  定义队头 int rear;//吃东西 定义队尾 }

C#操作消息列队

首先安装消息队列MSMQ,在"计算机管理-服务和应用程序-消息队列-专用队列"中新建列队名称Demo: static void SendAndReceiveMsg() { MessageQueue mq =new MessageQueue(); mq.Path = @".\Private$\Demo"; //构造消息 Message msg =new Message(); msg.Body ="Hello MessageQueue"; //向队列

消息列队组件的一些特性比较

RabbitMQ 基于AMQP实现,传统的messaging queue系统实现,基于Erlang.老牌MQ产品了.AMQP协议更多用在企业系统内,对数据一致性.稳定性和可靠性要求很高的场景,对性能和吞吐量还在其次. Kafka linkedin开源的MQ系统,主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输,0.8开始支持复制,不支持事务,适合产生大量数据的互联网服务的数据收集业务. ZeroMQ 只是一个网络编程的Pattern库,将常见的网络请求

.NET异常 由于系统缓冲区空间不足或列队已满,不能执行套接字上的操作。

操作系统环境:Windows XP 异常描述:异常不定时.随机抛出.抛出异常的语句一般都是需要建立socket连接的语句. 原因:在XP系统下,默认最大的TCP端口号为5000,当超出该值时抛出如上异常. 解决方法: 1)启动注册表编辑器. 2)在注册表中,找到以下注册表子项: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 3)在编辑菜单上,单击新建,然后添加以下注册表项: 值名称: MaxUserPo