【NOIP 2017】Day2 T3 列队
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 列队的更多相关文章
- 【NOIP 2013 DAY2 T3】 华容道(spfa)
题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间. 小 ...
- LUGOU 3959 宝藏 (noip 2017 day2 T2)
传送门 解题思路 去年noip现在拿来写..思路还是听清楚的,记忆化搜索,f[S]表示现在选了集合S时的最小代价,dis[i]表示达到最优时i这个点的深度.f[S| (1< < i-1) ...
- 【NOIP 2015 DAY2 T3】 运输计划 (树链剖分-LCA)
题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家 ...
- NOIP2017 Day2 T3 列队(treap)
可以直接用treap上大模拟...n+1个treap维护n行的前m-1个点和最后一列. 需要支持删除一个点或者一段区间,而空间并不支持存下所有的点的时候,可以用一个点代替一个区间,记录区间首项的值和区 ...
- 「NOIP 2017」列队
题目大意:给定一个 $n times m$ 的方阵,初始时第 $i$ 行第 $j$ 列的人的编号为 $(i-1) times m + j$,$q$ 次给出 $x,y$,让第 $x$ 行 $y$ 列的人 ...
- NOIP 2017 列队 - Splay - 树状数组
题目传送门 传送点I 传送点II 题目大意 (家喻户晓的题目应该不需要大意) (我之前咋把NOIP 2017打成了NOIP 2018,好绝望) Solution 1 Splay 每行一颗Splay,没 ...
- 【游记】NOIP 2017
时间:2017.11.11~2017.11.12 地点:广东省广州市第六中学 Day1 T1:看到题目,心想这种题目也能放在T1? 这个结论我之前遇到过至少3次,自己也简单证明过.初见是NOIP200 ...
- NOIP 2017 解题报告
---恢复内容开始--- NOIP 2017 的题真的很难啊,怪不得当年我这个萌新爆零了(当然现在也是萌新)越学越觉得自己什么都不会. 想要成为强者要把这些好题都弄懂弄透 至少现在6道题我都比较陌生 ...
- 「雅礼集训 2017 Day2」解题报告
「雅礼集训 2017 Day2」水箱 我怎么知道这种题目都能构造树形结构. 根据高度构造一棵树,在树上倍增找到最大的小于约束条件高度的隔板,开一个 \(vector\) 记录一下,然后对于每个 \(v ...
随机推荐
- Log4j基础知识
Log4J是Apache的一个开放源代码的项目.通过使用Log4J,程序员可以控制日志信息输送的目的地,包括控制台,文件,GUI组件和NT事件记录器,也可以控制每一条日志的输出格式,或通过定义每一条日 ...
- 设计模式之Decorator(油漆工)(转)
Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这 ...
- android使用ARouter跳转activity(阿里巴巴开源的)
android使用ARouter跳转activity(阿里巴巴开源的) 使用ARouter方式,点击按钮跳转到其他activitypublic void buyOrSell(String str){ ...
- SQL注入(dvwa环境)
首先登录DVWA主页: 1.修改安全级别为LOW级(第一次玩别打脸),如图中DVWA Security页面中. 2.进入SQL Injection页面,出错了.(心里想着这DVWA是官网下的不至于玩不 ...
- sqlchemy self made
# -*- coding: utf-8 -*- from sqlalchemy import create_engine, Column, String, Integer, ForeignKey, T ...
- Prometheus监控学习笔记之PromQL 内置函数
概述 Prometheus 提供了其它大量的内置函数,可以对时序数据进行丰富的处理.某些函数有默认的参数,例如:year(v=vector(time()) instant-vector).其中参数 v ...
- 腾讯云 网站开启HTTPS
下图是我站点的初始化样子,可以看到只是输出一个字符串,啥也没有,并且没有https. 这无所谓,因为我们的重点是https,而不是网站内容 接下来就是配置https的关键步骤了,其实只需要三步而已: ...
- ElasticSearch(二)CentOs6.4下安装ElasticSearch
一.准备工作 Elastic 需要 Java 8 环境.如果你的机器还没安装 Java,先需要安装java环境,同时还注意要保证环境变量JAVA_HOME正确设置. 链接:https://pan.ba ...
- 单元测试Mock框架Powermockito 【mockito1.X】
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> &l ...
- Spring Boot 2 (二):Spring Boot 2 动态 Banner
Spring Boot 2 (二):Spring Boot 2 动态 Banner Spring Boot 2.0 提供了很多新特性,其中就有一个小彩蛋:动态 Banner. 一.配置依赖 使用 Sp ...