「NOIP 2017」列队
题目大意:给定一个 $n times m$ 的方阵,初始时第 $i$ 行第 $j$ 列的人的编号为 $(i-1) times m + j$,$q$ 次给出 $x,y$,让第 $x$ 行 $y$ 列的人出队,然后其他人先向左看齐,后向前看齐,再把出队的人放在第 $n$ 行 $m$ 列,请你输出每次出队的人的编号。$n,m,q leq 3 times 10^5$
对于 $n,m leq 50000, q leq 500$ 的数据,可以离散化,但是不能用 map,因为 map 的所有操作都是带 log 的。
考虑 $q leq 500$,即最多涉及到 $500$ 个行,于是可以将行离散化,开一个 $500 times 50000$ 的数组,最后一列单独开一个 $50000 times 1$ 的数组。
对于所有 $x=1$ 的数据,可以用 treap,注意在 maintain 函数中所加的是 $1$ 而不是 $num[cur]$ 时,一定要加上 $cur neq 0$ 的判断。
(顺便提一下线段树做法:用 $0,1$ 表示该位置有没有人,初始时 $1 cdots n+m-1$ 的位置都是 $1$,每删一个元素就把它变成 $0$,把它新插入的位置变成 $1$,查询第 $i$ 个元素就是查询前缀和等于 $i$ 的位置)
#include <cstdio>
#include <cstdlib>
const int N = 1000000;
typedef long long LL;
int siz[N], rnd[N], ch[N][2], root, cnt; LL id[N], ans;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
void newNode(int &cur, LL x) {
cur = ++cnt, id[cur] = x;
siz[cur] = 1, rnd[cur] = rand();
}
void maintain(int cur) {
if (cur) siz[cur] = siz[ch[cur][0]] + siz[ch[cur][1]] + 1; //一定要加if(cur)的判断
}
void rotate(int &cur, int k) {
int fa = ch[cur][k^1];
ch[cur][k^1] = ch[fa][k], ch[fa][k] = cur;
maintain(cur), maintain(fa), cur = fa;
}
void insert(int &cur, LL x) {
if (!cur) newNode(cur, x);
else {
insert(ch[cur][1], x);
if (rnd[ch[cur][1]] > rnd[cur]) rotate(cur, 0);
maintain(cur);
}
}
void del(int &cur) {
if (!ch[cur][0]) cur = ch[cur][1];
else if (!ch[cur][1]) cur = ch[cur][0];
else {
int k = rnd[ch[cur][0]] > rnd[ch[cur][1]] ? 0 : 1;
rotate(cur, k), del(ch[cur][k]), maintain(cur);
}
}
void kth(int &cur, int k) { //查找第k个元素并删除
if (!cur) return;
if (siz[ch[cur][0]] == k - 1) ans = id[cur], del(cur);
else if (siz[ch[cur][0]] >= k) kth(ch[cur][0], k);
else kth(ch[cur][1], k - siz[ch[cur][0]] - 1);
maintain(cur);
}
int main() {
int n = read(), m = read(), T = read();
for (int i = 1; i <= m; ++i) insert(root, i);
for (int i = 2; i <= n; ++i) insert(root, 1LL * i * m);
while (T--) {
int x = read(), y = read();
kth(root, y);
printf("%lldn", ans);
insert(root, ans);
}
return 0;
}
平衡树
对于 $n,m,q leq 3 times 10^5$ 的数据,可以每一行都开一个 treap,最后一列单独开一个 treap,treap 中每个节点是一段连续的区间,且区间内的元素编号是一个等差数列,维护每个区间的第一个元素编号以及区间的大小,每次用插入操作模拟区间分裂即可。
#include <cstdio>
#include <cstdlib>
typedef long long LL;
const int N = 1500005, M = 300005;
int siz[N], num[N], rnd[N], ch[N][2], root[M], cnt; LL id[N], ans;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + 大专栏 「NOIP 2017」列队an class="p">(x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
void newNode(int &cur, LL x, int y) {
cur = ++cnt;
siz[cur] = num[cur] = y;
id[cur] = x, rnd[cur] = rand();
}
void maintain(int cur) {
siz[cur] = num[cur] + siz[ch[cur][0]] + siz[ch[cur][1]];
}
void rotate(int &cur, int k) {
int fa = ch[cur][k^1];
ch[cur][k^1] = ch[fa][k], ch[fa][k] = cur;
maintain(cur), maintain(fa), cur = fa;
}
void insert(int &cur, LL x, int y, int k) { //x表示块首元素的编号, y表示块的大小, k表示插入队首/队尾
if (!cur) newNode(cur, x, y);
else {
insert(ch[cur][k], x, y, k);
if (rnd[ch[cur][k]] > rnd[cur]) rotate(cur, k ^ 1);
maintain(cur);
}
}
void kth(int &cur, int k, int d) { //d表示公差
if (siz[ch[cur][0]] + 1 <= k && k <= siz[ch[cur][0]] + num[cur]) {
ans = id[cur] + 1LL * (k - siz[ch[cur][0]] - 1) * d;
if (ans == id[cur]) { //删除的是块首
id[cur] += d, --num[cur];
} else {
if (siz[ch[cur][0]] + num[cur] > k) //删除的不是块尾
insert(ch[cur][1], ans + d, siz[ch[cur][0]] + num[cur] - k, 0);
num[cur] = k - siz[ch[cur][0]] - 1;
}
} else if (siz[ch[cur][0]] >= k) {
kth(ch[cur][0], k, d);
} else {
kth(ch[cur][1], k - siz[ch[cur][0]] - num[cur], d);
}
maintain(cur);
}
int main() {
int n = read(), m = read(), q = read();
for (int i = 1; i <= n; ++i) insert(root[i], 1LL * (i - 1) * m + 1, m - 1, 1);
insert(root[n+1], m, n, 1);
while (q--) {
int x = read(), y = read();
if (y < m) {
kth(root[x], y, 1);
insert(root[n+1], ans, 1, 1);
printf("%lldn", ans);
kth(root[n+1], x, m);
insert(root[x], ans, 1, 1);
} else {
kth(root[n+1], x, m);
insert(root[n+1], ans, 1, 1);
printf("%lldn", ans);
}
}
return 0;
}
时间复杂度 $O(q log m)$
线段树
这个做法非常神仙……
比如一个 $3 times 5$ 的矩阵,我们有这样 $4$ 组询问:
1 3
1 2
3 1
1 4
用 $4$ 表示最后一列,那么所有的出队情况如下:
1 3
4 1
1 2
4 1
3 1
4 3
1 4
4 1
然后把它们分为三组:
1 3
1 2
1 4
3 1
4 1
4 1
4 3
4 1
用一棵线段树分别计算每行第 $i$ 个出队的人的位置,用 $1 cdots m-1$ 表示初始时的位置,$m cdots q$ 表示后来插入的位置,每走一个人就把他的位置变成 $0$,每次查询前缀和为 $i$ 的位置。如图以第一行为例:
其他行和最后一列也同理,都是用同一棵线段树来操作,但是不需要每次都 $memset$,只需要把上一次操作减去的值再加回来即可,因为是一共有 $q$ 次操作,所以最多加 $q$ 次。以下是每行每次出队的元素的位置:
3 (1)
2 (1)
6 (1)
1 (3)
1 (4)
2 (4)
5 (4)
3 (4)
将它们按照询问顺序排序:
3 (1)
1 (4)
2 (1)
2 (4)
1 (3)
5 (4)
6 (1)
3 (4)
对于每一行,我们开一个 $vector$ 储存后加入的元素(当然用邻接表更好),具体的操作如下:
- 对于第 $i$ 行,如果当前出队的元素位置 $j$ $<m$,则直接输出 $(i-1)m+j$,否则输出当前行的 $vector$ 中第 $j-m+1$ 个元素
- 输出完后,将该元素 $pushtext{_}back$ 到对应行(列)的 $vector$ 中
时间复杂度 $O(qlog(q+m))$
「NOIP 2017」列队的更多相关文章
- 「THUSCH 2017」大魔法师 解题报告
「THUSCH 2017」大魔法师 狗体面太长,帖链接了 思路,维护一个\(1\times 4\)的答案向量表示\(A,B,C,len\),最后一个表示线段树上区间长度,然后每次的操作都有一个转移矩阵 ...
- 「THUWC 2017」随机二分图
「THUWC 2017」随机二分图 解题思路 : 首先有一个 \(40pts\) 的做法: 前 \(20pts\) 暴力枚举最终的匹配是怎样的,check一下计算方案数,后 \(20pts\) 令 \ ...
- LOJ 2288「THUWC 2017」大葱的神力
LOJ 2288「THUWC 2017」大葱的神力 Link Solution 比较水的提交答案题了吧 第一个点爆搜 第二个点爆搜+剪枝,我的剪枝就是先算出 \(mx[i]\) 表示选取第 \(i \ ...
- 「NOIP 2020」微信步数(计数)
「NOIP 2020」微信步数(Luogu P7116) 题意: 有一个 \(k\) 维场地,第 \(i\) 维宽为 \(w_i\),即第 \(i\) 维的合法坐标为 \(1, 2, \cdots, ...
- @loj - 2977@ 「THUSCH 2017」巧克力
目录 @description@ @solution@ @accepted code@ @details@ @description@ 「人生就像一盒巧克力,你永远不知道吃到的下一块是什么味道.」 明 ...
- 【NOIP 2017】列队
Description Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×m名学生,方阵的行数为 n ...
- 「LOJ 2289」「THUWC 2017」在美妙的数学王国中畅游——LCT&泰勒展开
题目大意: 传送门 给一个动态树,每个节点上维护一个函数为$f(x)=sin(ax+b)$.$f(x)=e^{ax+b}$.$f(x)=ax+b$中的一个. 支持删边连边,修改节点上函数的操作. 每次 ...
- LOJ #2978「THUSCH 2017」杜老师
听说LOJ传了THUSC题赶紧上去看一波 随便点了一题都不会做想了好久才会写暴力爆了一发过了... LOJ #2978 题意 $ T$次询问,每次询问$ L,R$,问有多少种选取区间中数的方案使得选出 ...
- LOJ 2409「THUPC 2017」小 L 的计算题 / Sum
思路 和玩游戏一题类似 定义\(A_k(x)=\sum_{i=0}^\infty a_k^ix^i=\frac{1}{1-a_kx}\) 用\(\ln 'x\)代替\(\frac{1}{x}\), 所 ...
随机推荐
- 使用conda管理python环境
一.动机 最近打算折腾vn.py,但只有py27版本的,因为一向习惯使用最新稳定版的,所以不得不装py27的环境,不得不说 Python的全局锁真的很烦. 身为懒癌患者,必然使用全功能的anacond ...
- GCC的分支预测优化__builtin_expect
智能指针笔记 GCC的原子操作函数 将流水线引入cpu,可以提高cpu的效率.更简单的说,让cpu可以预先取出下一条指令,可以提供cpu的效率.如下图所示: 取指令 执行指令 输出结果 取指令 执行 ...
- 搭建rocketmq
安装maven和java环境,此处省略.如果没有安装,请先安装maven和java环境!或者安装openjdk 首先下载rockermq官方地址:http://rocketmq.apache.org/ ...
- SpringBoot 1.5.x 集成 Quartz 任务调度框架
Quartz 有分 内存方式 和 数据库方式 内存方式任务信息保存在内存中, 停机会丢失, 需手动重新执行, 数据库方式: 任务信息保存在数据库中, 重点是支持集群. 内存方式 RAMJobStore ...
- python语法基础-并发编程-协程-长期维护
############### 协程 ############## # 协程 # 小知识点, # 协程和进程和线程一样都是实现并发的手段, # 开启一个线程,创建一个线程,还是需要开销, ...
- python语法基础-文件操作-长期维护
############### python-简单的文件操作 ############### # python中文件的操作 # 文件操作的基本套路 # 1,打开文件,默认是是只读方式打开文件 ...
- vue2.0学习之组件间通信
/* child.vue*/ 子组件 <template> <div> /*必须要用div包裹起来,然后在里面写需要的组件内容,这里面和平常写的html是一样的*/ <d ...
- 让Spring不再难懂-aop篇
什么是aop AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP允许 ...
- 【UML】
静态:类图,包图,部署图,构件图,对象图 行为:用例图,活动图,顺序图,状态图,交互图 [类图] http://www.uml.org.cn/oobject/201104212.asp [对象图] h ...
- Learn Git Lesson06 - 分离头指针
============== 知识点 分离头指针 HEAD 含义 git diff 分离头指针 (Detached HEAD) 有时候想尝试性修改某些内容(实验),也许并不会真的提交到分支,这时候可以 ...