Problem

Description

\(Sylvia\) 是一个热爱学习的女孩子。

前段时间,\(Sylvia\) 参加了学校的军训。众所周知,军训的时候需要站方阵。

\(Sylvia\) 所在的方阵中有\(n*m\)名学生,方阵的行数为 \(n\),列数为 \(m\)。

为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中 的学生从 \(1\) 到 \(n*m\) 编上了号码(参见后面的样例)。即:初始时,第 \(i\) 行第 \(j\) 列 的学生的编号是\((i-1)*m + j\)。

然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天 中,一共发生了 \(q\) 件这样的离队事件。每一次离队事件可以用数对\((x,y)(1≤x≤n,1≤y≤m)\)描述,表示第 \(x\) 行第 \(y\) 列的学生离队。

在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达 这样的两条指令:

向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条 指令之后,空位在第 \(x\) 行第 \(m\) 列。

向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条 指令之后,空位在第 \(n\) 行第 \(m\) 列。

教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后, 下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 \(n\) 行 第 \(m\) 列一个空位,这时这个学生会自然地填补到这个位置。

因为站方阵真的很无聊,所以 \(Sylvia\) 想要计算每一次离队事件中,离队的同学 的编号是多少。

注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后 方阵中同学的编号可能是乱序的。

Input Format

输入共 \(q+1\) 行。

第 1 行包含 3 个用空格分隔的正整数 \(n,m,q\),表示方阵大小是 \(n\) 行 \(m\) 列,一共发 生了 \(q\) 次事件。

接下来 \(q\) 行按照事件发生顺序描述了 \(q\) 件事件。每一行是两个整数 \(x,y\),用一个空 格分隔,表示这个离队事件中离队的学生当时排在第 \(x\) 行第 \(y\) 列。

Output Format

按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学 生的编号。

Sample

Input:

2 2 3
1 1
2 2
1 2

Output:

1
1
4

Explanation

Explanation for Input

列队的过程如上图所示,每一行描述了一个事件。 在第一个事件中,编号为 \(1\) 的同学离队,这时空位在第一行第一列。接着所有同学 向左标齐,这时编号为 \(2\) 的同学向左移动一步,空位移动到第一行第二列。然后所有同 学向上标齐,这时编号为 \(4\)的同学向上一步,这时空位移动到第二行第二列。最后编号 为 \(1\) 的同学返回填补到空位中。

Range

测试点编号 $n$ $m$ $q$ 其他约定
$1\sim 6$ $\le 1000$ $\le 1000$ $\le 500$
$7\sim 10$ $\le 5 \times 10^{4}$ $\le 5 \times 10^{4}$
$11,12$ $=1$ $\le 10^{5}$ $\le 10^{5}$ 所有事件 $x=1$
$13,14$ $\le 3\times 10^{5}$ $\le 3\times 10^{5}$
$15,16$ $\le 3 \times 10^{5}$
$17,18$ $\le 10^{5}$ $\le 10^{5}$ $\le 10^{5}$
$19,20$ $\le 3 \times 10^{5}$ $\le 3 \times 10^{5}$ $\le 3 \times 10^{5}$

数据保证每一个事件满足\(1\leq x \leq n,1\leq y \leq m\)

Algorithm

  • 线段树/树状数组,就看你操作骚不骚强不强。如果不会线段树操作,那么平衡树也是很可以的。。。

Notice

  • 线段树的赋值维护仔细点= =

  • 注意编号要开\(long\ long\)

  • 思维一定要清晰,不然会被搞懵的。

Mentality

我觉得这是\(NOIP\)史上最\(shi\)的\(Day2~T3\)= =。\(18\)年的都没这个玩意\(shi\)。

首先看到数据范围,\(3e5\) 是挺坑的。。接着我们可以想到,因为每次出队的人必定都会去到最后一列,那么我们是不是可以维护最后一列,最后只需要输出最后一列所有历史状态的并呢?

当然,并不需要维护一个历史状态,其实我们只需要做到当某一个数向左看齐进入某一行的\(m-1\)列时不删除这个数,只是当做这个数不存在了,也就是让它对后面的数不再产生影响即可。可以理解为把出队元素全部放入一个栈。大致如下:

我们假设如下有五个操作:

OPT1:(2,1)出队

这里我们注意,对于新进入的元素\(3\),我们不应该把它叫做\(3\),而是一个指针 \(->1\),代表它指向总最后一列中的第一个位置,如下:

OPT2:(2,2)出队

同理,我们把 \(9\) 也写成一个指针 \(->3\) :

OPT3:(2,2)出队

OPT4:

OPT5:

对于总最后一列,我们这样处理:

  • Step 0

依次由下往上将指针修改为指向位置的值:

  • Step 1

  • Step 2

由于统计所有出队的人即可,我们输出总最后一列中的\(m+1\) ~ \(m+q\)位即可

虽然关于向左看齐的维护用平衡树可以快乐地做到,但是不会啊。。。

接着想想办法维护这个总的最后一列。我们会发现,最后一列必定由原本的最后一列的元素加上每次离队的元素得到。那么接着考虑一下发现,最后一列里的元素可能会有重复。即原本已经出过队的人再一次出队。这我们可以让新离队补充的位置指向之前的位置,最后输出的时候判断更新即可。那么问题在于,我们如何维护最后一列?

我们可以观察到,用平衡树依次维护每次最后一列的向左看齐是可以做到保证能知道当时出列的那个数处于总最后列的哪个位置。那么线段树怎么做呢?我们考虑为每个数都赋一个权值,如果这个数在当前的第\(m\)列的\(n\)个数中,那么它的权值为\(1\),反之为\(0\)。每次向左看齐,如果一个元素脱离了第\(m\)列,那么它在总最后一列中的权值赋为\(0\),对于下一个进入最后一列的元素赋值为\(1\)。如果我们需要寻找此刻最后一列的第\(k\)个元素,我们只需要在总最后一列中寻找第\(k\)个权值为\(1\)的数。这个通过记录\(sum\)在线段树上查找即可。转换成之前的总最后一列的图,我们可以理解为:权值为1的点为红色,否则为黑色,那么序列中的第\(k\)个元素即为从下到上第\(k\)个红色的点。只不过对于每一行我们是横向维护,所以是从左到右第\(k\)个红色元素为当前第\(k\)个

首先,我们根据询问顺序来看,处理出每次询问后当前最后一列里出去了哪个数并记录在相关行,然后我们就可以知道每次向左看齐离开最后一列的数在总最后一列的位置了。

那么接着我们离线处理询问,以\(x\)为第一关键词,询问为第二关键词排序处理,对于每一行,我们单独进行处理。我们把每次向左看齐进来的数记录它在总序列中的位置。当我们处理一个询问时,就像对最后一列的处理一样维护一个\(01\)的权值串即可找出是在当前这一行第\(k\)个位置,这一行原来的数加上新添的数的总序列中的第几个。如果这个元素是这一行中本来就有的元素,我们直接将它加入总最后一列的相应位置,对于第\(i\)个询问,这个位置显然是\(n+i\)。否则,我们在总最后一列相应位置放一个标记,同时值为这个外来的元素之前本身在最后一列中的所在。

最后输出询问,假设我维护的最后一列为数组\(s\),指向标记为\(book\),那么输出如下:

for(int i=n+1;i<=n+q;i++)
{
if(book[i])
s[i]=s[s[i]];
printf("%lld\n",s[i]);
}

完成!

Code

#include <algorithm>
#include <cstdio>
#include <iostream>
using namespace std;
int n, m, Q, sum[2400001], x, adv[2400001], bj[2400001], L, R;
long long s[600001];
bool book[600001];
struct node {
int x, y, d, next;
} k[300002];
bool cmp(node a, node b) {
if (a.x == b.x) return a.d < b.d;
return a.x < b.x;
}
void pushdown(int o, int l, int r) {
if (adv[o] != -1) {
int mid = (l + r) / 2;
adv[o * 2] = adv[o * 2 + 1] = adv[o];
sum[o * 2] = (mid - l + 1) * adv[o];
sum[o * 2 + 1] = (r - mid) * adv[o];
adv[o] = -1;
}
}
void pushup(int o) { sum[o] = sum[o * 2] + sum[o * 2 + 1]; }
void add(int o, int l, int r) {
if (l >= L && r <= R) {
sum[o] = x * (r - l + 1);
adv[o] = x;
return;
}
pushdown(o, l, r);
int mid = (l + r) / 2;
if (mid >= L) add(o * 2, l, mid);
if (mid < R) add(o * 2 + 1, mid + 1, r);
pushup(o);
}
void query(int o, int l, int r) {
int mid = (l + r) / 2;
if (l == r) {
x = r;
return;
}
pushdown(o, l, r);
if (sum[o * 2] < R) {
R -= sum[o * 2];
query(o * 2 + 1, mid + 1, r);
} else
query(o * 2, l, mid);
pushup(o);
}
void work(int l, int r, int X) {
sum[1] = 0;
adv[1] = 0;
L = 1, R = m;
x = 1;
add(1, 1, m + Q);
int xl[300001];
for (int i = l; i <= r; i++)
xl[i - l] = k[i].next; //即将在询问前加入的r-l+1个外来数的位置指针
for (int i = l; i <= r; i++) {
R = k[i].y;
query(1, 1, m + Q); //查询第k个元素
if (x < m)
s[k[i].d + n] = (long long)(X - 1) * m + x; //队内元素加入总最后一列
else {
book[k[i].d + n] = 1; //标记这个位置是指针
s[k[i].d + n] = xl[x - m]; //指针赋值
}
L = x, R = x;
x = 0;
add(1, 1, m + Q); //权值清零
L = m + i - l + 1, R = m + i - l + 1;
x = 1;
add(1, 1, m + Q); //权值赋1
}
}
int main() {
cin >> n >> m >> Q;
for (int i = 1; i <= max(n, m) + Q; i++) adv[i] = -1;
L = 1, R = n, x = 1;
add(1, 1, n + Q);
for (int i = 1; i <= n; i++)
s[i] = (long long)i * (long long)m; //总最后一列的前m个数
for (int i = 1; i <= Q; i++) {
scanf("%d%d", &k[i].x, &k[i].y);
k[i].d = i;
R = k[i].x;
query(1, 1, n + Q); //查找第k个在序列中的元素
k[i].next = x;
L = x, R = x;
x = 0;
add(1, 1, n + Q); //元素出队,权值清零
L = n + i, R = n + i;
x = 1;
add(1, 1, n + Q); //元素入队,权值为1
}
sort(k + 1, k + Q + 1, cmp); //离线询问
int head = 1;
for (int i = 2; i <= Q + 1; i++)
if (k[i].x != k[i - 1].x) {
work(head, i - 1, k[i - 1].x); //对于每一行单独处理
head = i;
}
for (int i = n + 1; i <= n + Q; i++) {
if (book[i]) s[i] = s[s[i]]; //指针定位
printf("%lld\n", s[i]); //输出
}
}

【NOIP 2017】Day2 T3 列队的更多相关文章

  1. 【NOIP 2013 DAY2 T3】 华容道(spfa)

    题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间. 小 ...

  2. LUGOU 3959 宝藏 (noip 2017 day2 T2)

    传送门 解题思路 去年noip现在拿来写..思路还是听清楚的,记忆化搜索,f[S]表示现在选了集合S时的最小代价,dis[i]表示达到最优时i这个点的深度.f[S| (1< < i-1) ...

  3. 【NOIP 2015 DAY2 T3】 运输计划 (树链剖分-LCA)

    题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家 ...

  4. NOIP2017 Day2 T3 列队(treap)

    可以直接用treap上大模拟...n+1个treap维护n行的前m-1个点和最后一列. 需要支持删除一个点或者一段区间,而空间并不支持存下所有的点的时候,可以用一个点代替一个区间,记录区间首项的值和区 ...

  5. 「NOIP 2017」列队

    题目大意:给定一个 $n times m$ 的方阵,初始时第 $i$ 行第 $j$ 列的人的编号为 $(i-1) times m + j$,$q$ 次给出 $x,y$,让第 $x$ 行 $y$ 列的人 ...

  6. NOIP 2017 列队 - Splay - 树状数组

    题目传送门 传送点I 传送点II 题目大意 (家喻户晓的题目应该不需要大意) (我之前咋把NOIP 2017打成了NOIP 2018,好绝望) Solution 1 Splay 每行一颗Splay,没 ...

  7. 【游记】NOIP 2017

    时间:2017.11.11~2017.11.12 地点:广东省广州市第六中学 Day1 T1:看到题目,心想这种题目也能放在T1? 这个结论我之前遇到过至少3次,自己也简单证明过.初见是NOIP200 ...

  8. NOIP 2017 解题报告

    ---恢复内容开始--- NOIP 2017 的题真的很难啊,怪不得当年我这个萌新爆零了(当然现在也是萌新)越学越觉得自己什么都不会. 想要成为强者要把这些好题都弄懂弄透 至少现在6道题我都比较陌生 ...

  9. 「雅礼集训 2017 Day2」解题报告

    「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...

随机推荐

  1. python 文件描述符

    先上一张图 文件描述符是内核为了高效管理已经被打开的文件所创建的索引, ----非负整数 ----用于指代被打开的文件 ----所有执行i/o操作的系统调用都是通过文件描述符完成的 进程通过文件描述符 ...

  2. Collections集合工具类的方法

    addAll & shuffle: 返回类型为boolean类型,执行完操作不接收也行: 其中,静态方法,与对象无关,类名点方法名直接调用: 点点点为可变参数,随便填写几个参数都可以: sor ...

  3. java获取前一天时间SimpleDateFormat,java判断某个时间段

    java获取前一天时间SimpleDateFormat SimpleDateFormat predf = new SimpleDateFormat("yyyy-MM-dd"); D ...

  4. [转载]dbms_lob用法小结

    http://blog.sina.com.cn/s/blog_713978a50100prkt.html CLOB里存的是2进制 判定长度   DBMS_LOB.GETLENGTH(col1)获取文本 ...

  5. TestNG 搭建测试框架 自动化测试

    框架层级及基本组件:    参考:https://www.cnblogs.com/jier888/p/8998724.html Java作为开发语言 Maven管理项目及Jar包 Testng作为测试 ...

  6. 在vim编辑器python实现tab补全功能

    在vim编辑器中实现python tab补全插件有Pydiction,Pydiction可以实现下面python代码的自动补全: 1. 简单python关键词补全 2. python函数补全带括号 3 ...

  7. goldengate 12.3 实现mysql数据及DDL实时同步

    以下环境在mysql 5.7上完成. set mysql_home=mysql安装路径 set path=%mysql_home%\bin;%path% 首先要准备mysql的启动,可参考:http: ...

  8. 判断闰年C语言版

    #include<stdio.h> int isLeap(int year) { // 必须先判断是平年的情况 后判断闰年的情况 == && year%!=) || yea ...

  9. django图书管理系统实例

    首页,其他页面全部继承首页的上半部分 点击发布图书页面 首页点击书名,跳转到图书信息界面,该界面可删除图书 项目结构 #views.py from django.shortcuts import re ...

  10. UI简述

    UI的全称是user interface,即是用户界面.UI设计是指对软件的人机交互,操作编辑,界面美观的整体设计,从简单的角度来说就是,UI是视觉上的东西,包括logo.软件.网页的按钮.网页导航, ...