洛谷 P3960 列队 解题报告
P3960 列队
题目描述
\(Sylvia\)是一个热爱学习的女♂孩子。
前段时间,\(Sylvia\)参加了学校的军训。众所周知,军训的时候需要站方阵。
\(Sylvia\)所在的方阵中有\(n \times m\)名学生,方阵的行数为\(n\),列数为\(m\)。
为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从\(1\)到\(n \times m\)编上了号码(参见后面的样例)。即:初始时,第\(i\)行第\(j\)列 的学生的编号是\((i-1)\times m + j\)。
然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了\(q\)件这样的离队事件。每一次离队事件可以用数对\((x,y) (1 \le x \le n, 1 \le y \le m)\)描述,表示第\(x\)行第\(y\)列的学生离队。
在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:
向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第\(x\)行第\(m\)列。
向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第\(n\)行第\(m\)列。
教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第\(n\)行 第\(m\)列一个空位,这时这个学生会自然地填补到这个位置。
因为站方阵真的很无聊,所以\(Sylvia\)想要计算每一次离队事件中,离队的同学 的编号是多少。
注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。
输入输出格式
输入格式:
输入共 \(q+1\) 行。
第 1 行包含 3 个用空格分隔的正整数\(n, m, q\),表示方阵大小是\(n\)行\(m\)列,一共发 生了\(q\)次事件。
接下来\(q\)行按照事件发生顺序描述了\(q\)件事件。每一行是两个整数\(x,y\),用一个空格分隔,表示这个离队事件中离队的学生当时排在第\(x\)行第\(y\)列。
输出格式:
按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学 生的编号。
思路:
用平衡树点分裂的写法。
我这里采用的是\(splay\),维护左右区间\(L,R\),子树大小\(siz\),两个儿子和父亲。
每一行维护一颗,最后一列维护一颗
查询时要分裂就分裂,放到最后一列,再把最后一列的放过来
我犯的细节问题(调+拍了好久):
- 把节点插在最右边也要旋转!!!因为是双旋所以对树的深度有平衡作用
- 分裂时传人区间第几个时注意减去左子树的大小
Code:
#include <cstdio>
#include <cstring>
#define ls ch[now][0]
#define rs ch[now][1]
#define f par[now]
#define ll long long
const ll N=2500010;
ll ch[N][2],par[N],siz[N],L[N],R[N];
ll root[N],n,m,q,tot,out;
ll identity(ll now)
{
return ch[f][1]==now;
}
void connect(ll fa,ll now,ll typ)
{
f=fa;
ch[fa][typ]=now;
}
void updata(ll now)
{
siz[now]=siz[ls]+siz[rs]+R[now]+1-L[now];
}
void Rotate(ll now)
{
ll p=f,typ=identity(now);
connect(p,ch[now][typ^1],typ);
connect(par[p],now,identity(p));
connect(now,p,typ^1);
updata(p);updata(now);
}
void splay(ll now,ll to,ll id)
{
to=par[to];
for(;f!=to;Rotate(now))
if(par[f]!=to)
Rotate(identity(now)^identity(f)?now:f);
if(!to) root[id]=now;
}
ll New(ll l,ll r)
{
siz[++tot]=r+1-l;L[tot]=l;R[tot]=r;
return tot;
}
ll split(ll now,ll pos)//返回分裂出的那个点
{
siz[now]--;
if(out) printf("%lld\n",L[now]+pos-1);
if(pos==1)
{
L[now]++;
return New(L[now]-1,L[now]-1);
}
if(pos==R[now]+1-L[now])
{
R[now]--;
return New(R[now]+1,R[now]+1);
}
ll Ls=ls;
connect(now,New(L[now],L[now]+pos-2),0);
connect(ls,Ls,0);
updata(ls);
L[now]=L[now]+pos;
updata(now);
return New(L[now]-1,L[now]-1);
}
ll get_right(ll now,ll is)
{
if(is) siz[now]++;
return rs?get_right(rs,is):now;
}
ll Find(ll id,ll pos)//返回要拿走的点
{
ll now=root[id];
while(233)
{
if(siz[ls]>=pos) now=ls;
else if(siz[now]-siz[rs]<pos) pos-=siz[now]-siz[rs],now=rs;
else
{
pos-=siz[ls];
splay(now,root[id],id);
if(L[now]==R[now])
{
if(out) printf("%lld\n",L[now]);
if(ls)
{
ll rt=get_right(ls,0);
splay(rt,ls,id);
connect(root[id]=rt,rs,1);
updata(rt);
}
else root[id]=rs;
par[root[id]]=0;
ls=rs=0,siz[now]=1;
return now;
}
return split(now,pos);
}
}
}
void put(ll num,ll id)//把啥子编号的点放在某棵树最右边
{
if(!root[id]) root[id]=num;
else
{
connect(get_right(root[id],1),num,1);
splay(num,root[id],id);
}
}
void Operator(ll line,ll row)
{
if(row!=m)
{
out=1;put(Find(line,row),n+1);
out=0;put(Find(n+1,line),line);
}
else
out=1,put(Find(n+1,line),n+1);
}
ll build(ll l,ll r)
{
if(l>r) return 0;
if(l==r) return New(l*m,l*m);
ll mid=l+r>>1;
ll now=New(mid*m,mid*m);
ls=build(l,mid-1);
par[ls]=now;
rs=build(mid+1,r);
par[rs]=now;
updata(now);
return now;
}
void init()
{
scanf("%lld%lld%lld",&n,&m,&q);
for(ll i=1;i<=n;i++)
root[i]=New(m*(i-1)+1,m*i-1);
root[n+1]=build(1,n);
}
void work()
{
ll x,y;
for(ll i=1;i<=q;i++)
{
scanf("%lld%lld",&x,&y);
Operator(x,y);
}
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("wr.out","w",stdout);
init();
work();
return 0;
}
2018.7.27
洛谷 P3960 列队 解题报告的更多相关文章
- 洛谷 P2058 海港 解题报告
P2058 海港 题目描述 小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客. 小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况: ...
- 洛谷P3960 列队 NOIp2017 线段树/树状数组/splay
正解:动态开点线段树 解题报告: 传送门! 因为最近学主席树的时候顺便get到了动态开点线段树?刚好想起来很久很久以前就想做结果一直麻油做的这题,,,所以就做下好了QAQ 然后说下,这题有很多种方法, ...
- 洛谷P3960 列队(NOIP2017)(Splay)
洛谷题目传送门 最弱的Splay...... 暴力模拟30分(NOIP2017实际得分,因为那时连Splay都不会)...... 发现只是一个点从序列里搬到了另一个位置,其它点的相对位置都没变,可以想 ...
- 洛谷 P3956 棋盘 解题报告
P3956 棋盘 题目描述 有一个\(m×m\)的棋盘,棋盘上每一个格子可能是红色.黄色或没有任何颜色的.你现在要从棋盘的最左上角走到棋盘的最右下角. 任何一个时刻,你所站在的位置必须是有颜色的(不能 ...
- 洛谷 P1979 华容道 解题报告
P1979 华容道 题目描述 小\(B\)最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时 ...
- BZOJ 3545 / 洛谷 P4197 Peaks 解题报告
P4197 Peaks 题目描述 在\(\text{Bytemountains}\)有\(N\)座山峰,每座山峰有他的高度\(h_i\).有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个 ...
- 虔诚的墓主人(BZOJ1227)(洛谷P2154)解题报告
题目描述 小W是一片新造公墓的管理人.公墓可以看成一块N×M的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地. 当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地. ...
- 洛谷 P3960 列队
https://www.luogu.org/problemnew/show/P3960 常数超大的treap #pragma GCC optimize("Ofast") #incl ...
- 洛谷 P2672 推销员 解题报告
P2672 推销员 题目描述 阿明是一名推销员,他奉命到螺丝街推销他们公司的产品.螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户.螺丝街一共有N家住户,第i家住户到入口的距离为 ...
随机推荐
- tensorflow学习一
1.用图(graph)来表示计算任务 2.用op(opreation)来表示图中的计算节点,图有默认的计算节点,构建图的过程就是在其基础上加节点. 3.用tensor表示每个op的输入输出数据,可以使 ...
- Jmeter断言、参数化及集合点
JMeter---QPS(Query Per Second) QPS为每秒查询率.是一台查询服务器每秒能够处理的查询次数,在因特网上,作为域名系统服务器的性能经常用每秒查询率来衡量.步骤:1.添加线程 ...
- mvc中actionresult的返回值类型
以前一直没注意actionresult都能返回哪些类型的类型值(一直用的公司的内部工具类初始化进行返回的),今天跟大家分享一下(也是转载的别人的日志qaq). 首先我们了解一下对action的要求: ...
- Java进阶——— 线程池的原理分析
前言 在了解线程池之前,其实首先出现的疑问是:为什么要使用线程池,其次是了解什么是线程池,最后是如何使用线程池,带着疑问去学习. 为什么要使用 前面多线程文章中,需要使用线程就开启一个新线程,简单方便 ...
- python常用命令—windows终端查看安装包信息
1, pip list 会将 Python 的所有安装包全部显示出来, 左边是包名, 右边是包的版本号. 2, pip show 包的名字 会将这个包的名字,版本号,包的功能说明,按装这个包的路径显示 ...
- 十分钟掌握pandas(pandas官方文档翻译)
十分钟掌握pandas 文档版本:0.20.3 这是一个对pandas简短的介绍,适合新用户.你可以在Cookbook中查看更详细的内容. 通常,我们要像下面一样导入一些包. In [1]: impo ...
- Python中的eval
Python中的eval方法接受一个字符串参数,并且把字符串里面的内容当成Python代码来执行: eval的缺点是执行速度慢,并且会有安全风险
- allocator类
一.动态数组 [new的局限性] new将内存分配和对象构造组合在一起,同样delete将对象析构和内存释放组合在一起 我们分配单个对象时,通常希望将内存分配和对象初始化组合在一起(我们知道对象应有什 ...
- 福大软工1816:Alpha(7/10)
Alpha 冲刺 (7/10) 队名:Jarvis For Chat 组长博客链接 本次作业链接 团队部分 团队燃尽图 工作情况汇报 张扬(组长) 过去两天完成了哪些任务: 文字/口头描述: 1.完成 ...
- Ubuntu16.04修改IP
首先用root用户登陆,然后输入你root的密码.如下图: 然后编辑interfaces文件,该文件位于/etc/network/下,执行如下命令: vim /etc/network/interf ...