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);} elsequery(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. UOJ 265 NOIP 2016 DAY2 T3 浅谈预处理状态压缩动态规划

    世界真的很大 要是当年我会一点状压该多好.. 虽然现在我状压也很差,但是还是把这道题水出来了 马上要NOIP2017了,抓紧调整状态才好 看题先: description Kiana 最近沉迷于一款神 ...

  2. [NOIp2017 Day2 T3] 列队phalanx(线段树 / 平衡树)

    题目 描述 Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×mn×mn \times m名学生,方 ...

  3. 【游记】NOIP 2017

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

  4. NOIP 2017 游记

    大家都写,我也来凑热闹 PS:用双拼可真是累啊.. (用双拼打了四行以后我决定以后再也不用双拼了!) DAY 0 颓废日,上午跑到机房颓废可真是爽 但是我坚持自我 day-2时下定决心买了阿里云的服务 ...

  5. #6034. 「雅礼集训 2017 Day2」线段游戏 李超树

    #6034. 「雅礼集训 2017 Day2」线段游戏 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统 ...

  6. NOIP 2017 Day1 T2 时间复杂度 complexity - 模拟题 题解

    作者@豪哒哒哒HaoDaDaDa 转载自简书@豪哒哒哒HaoDaDaDa-简书-NOIP 2017 Day1 T2 时间复杂度 (有一个月没有写简书了-) (这次终于开始拿Markdown写了,富文本 ...

  7. 在9012年1月22日观【NOIP 2017】

    总结:两句话 不会高级算法就搜呗 使劲搜,使劲剪 暴力整起嘛,搜索来起嘛 进入正题 NOIP 2017 D1T1 听说是一道数学竞赛原题 我可要感谢您啊CCF,在学信奥的同时也可以学数竞 现在我来说说 ...

  8. NOIP 2017 提高组 初赛

    NOIP 2017 提高组 初赛 做题感悟. 五.完善程序,是本张试卷最简单的题目,两道题做完,大约花了10分钟.2017-10-17 18:25 对了答案,破天荒,全对. 1.(大整数除法) 有如下 ...

  9. ◆竞赛题目◆◇NOIP 2017 普及组◇ 图书管理员

    ◇NOIP 2017 普及组◇图书管理员 Description 图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个 正整数. 每位借书的读者手中有一个需求码,这个需求码也是一个 ...

最新文章

  1. java 打电话_第四十二篇----拨打电话
  2. 自己写的小程序 deb打包
  3. JS操作Excel读取和写入(模板操作)
  4. QT乱码总结3.UNICODE有无BOM
  5. 服务器如何区分多个客户端的信息,如何从服务器向客户端发送已连接客户端的列表,以区分常规消息?...
  6. VS2005迁移项目工程所带来问题
  7. 基于范围的for循环
  8. python系列九:python3迭代器和生成器
  9. mysql数据库攻击与防御pdf_SQL注入攻击与防御 中文PDF清晰扫描版(38.6M)
  10. lan口配置 petalinux_【Xilinx-Petalinux学习】-02-建立PetaLinux工程
  11. R语言中的apply(),lapply(),sapply(),tapply()函数以及示例
  12. AUTOSAR Functional Safety
  13. 如何将音视频中的伴奏背景音乐和人声分离?
  14. 3GPP TS 29244-g30 中英文对照 | 5.2.2 Usage Reporting Rule Handling
  15. libgdx中文社区网正式上线了-libgdx.net
  16. 【C语言经典面试题】这样的char * 定义怎么回事
  17. 【UE4教程文档翻译】First Person Shooter C++ Tutoria(第一人称射击C++教程)
  18. 1.4 Python 程序语法元素分析
  19. 18 《社会性动物》 -豆瓣评分9.1
  20. python恶搞代码打开对方摄像头_大神们救命啊!!如何实现把摄像头读入的视频在tk界面呈现出来?...

热门文章

  1. cad中直径符号不显示_【技巧】大神在CAD、Word里敲出和快速绘图的小秘密!(内附cad快捷键大全)...
  2. DLNA介绍(包含UPnP,2011/6/20 更新)
  3. Charles ——抓包工具安装与(Charles与浏览器)配置(Python爬虫必备神器)
  4. copy() 与 clone() 的区别
  5. jsf中应用javascript
  6. QT报错:Gtk-Message:Failed to load module :gail“
  7. uAvionix获得FAA合同,部署和演示多个无人机同时飞行的C波段频率分配管理(FAM)
  8. 赛门铁克扩展验证EV SSL证书
  9. [系统安全] 二十二.PE数字签名之(下)微软证书漏洞CVE-2020-0601复现及Windows验证机制分析
  10. 窗口的创建CreateWindow/CreateWindowEx函数使用说明