Problem

Description

一次小 \(G\) 和小 \(H\) 原本准备去聚餐,但由于太麻烦了于是题面简化如下:

一个转盘上有摆成一圈的 \(n\) 个物品(编号 \(1\) 至 \(n\))其中第 \(i\) 个物品会在 \(T_i\) 时刻出现。

在 \(0\) 时刻时,小 \(G\) 可以任选 \(n\) 个物品中的一个,我们将其编号记为 \(s_0\)。并且如果 \(i\) 时刻选择了物品 \(s_i\),那么 \(i + 1\) 时刻可以继续选择当前物品或者选择下一个物品。当 \(s_i\) 为 \(n\) 时,下一个物品为物品 \(1\),否则下一个物品为 \(s_{i} + 1\)。在每一时刻(包括 \(0\) 时刻),如果小 \(G\) 所选择的物品已经出现了,那么小 \(G\) 将会标记它。小 \(H\) 想知道,在物品选择的最优策略下,小 \(G\) 什么时候能标记所有物品?

但麻烦的是,物品的出现时间会不时修改。我们将其描述为 \(m\) 次修改,每次修改将改变其中一个物品的出现时间。每次修改之后,你也需要求出当前局面的答案。对于其中部分测试点,小 \(H\) 还追加了强制在线的要求。

Input Format

第一行三个非负整数 \(n,m,p\),代表一共有 \(n\) 个物品,\(m\) 次修改。\(p\) 只有 \(0\) 或 \(1\) 两种取值,强制在线时 \(p\) 为 \(1\),否则为 \(0\)。本节后面将解释如何使用 \(p\)。

接下来一行,有 \(n\) 个用空格隔开的非负整数,第 \(i\) 个数 \(T_i\) 代表物品 \(i\) 的出现时间。

接下来 \(m\) 行,每行两个非负整数 \(x,y\),代表一次修改及询问。修改方式如下:

  • 如果 \(p = 0\),则表示物品 \(x\) 的出现时间 \(T_x\) 修改为 \(y\)。
  • 如果 \(p = 1\),则先将 \(x\) 和 \(y\) 分别异或 \(LastAns\) 得到 \(x′\) 和 \(y′\):即 \(x′ = x \oplus LastAns, y′ = y \oplus LastAns\)。然后将物品 \(x′\) 的出现时间 \(T_{x′}\) 修改为 \(y′\) 。其中的 \(LastAns\) 是前一个询问的答案;特别的,第一次修改时的 \(LastAns\) 为初始局面的答案。其中的 \(\oplus\) 为按位异或运算,例如 \(1 \oplus 2 = 3,4 \oplus 5 = 1,6 \oplus 11 = 13\)。

保证输入合法。

Output Format

第一行一个整数代表初始局面的答案。

接下来 \(m\) 行每行一个整数分别代表每次修改后的答案。

Sample

Input

  1. 5 3 0
  2. 1 2 3 4 5
  3. 3 5
  4. 5 0
  5. 1 4

Output

  1. 5
  2. 7
  3. 6
  4. 7

Range

测试点编号 $n$ $m$ $T_i/T_x$ $p$
1 $\le 10$ $=0$
2 $\le 1000$ $=0$ $\le 1000$
3 $\le 10^5$ $\le 10^5$
4 $\le 5000$
5 $\le 8\times 10^4$
6 $=1$
7 $\le 9\times 10^4$ $=0$
8 $=1$
9 $\le 10^5$ $=0$
10 $=1$

Algorithm

线段树,单调栈

Mentality

这题真是毒瘤 \(......\) (我觉得比毒瘤毒瘤)

我们先看看数据范围 \(......\) \(nlogn\) 预定。

先考虑一下小小地转化模型,因为要求的东西实在不好算。那么我们考虑把题目所述的过程倒过来看 \(......\) 那就变成了在环上往前走,每个点都会在某一时刻消失,在每个点消失前遍历所有点。

在转化模型之后,我们肯定能得出一个结论:中途停下永远不会更优!

先破环为链来思考。

对于一个位置为起点,因为我们必须要走遍整个环所有点,我们只需要计算一下从当前点出发最少需要多少时间来确保能够安全到达每个点。

则不难想到,我们从点 \(i\) 出发,遍历 \(i\sim i-n+1\) 所需要的最少时间肯定为:

\[Max_{i-n+1< j\le i}(T[j]+i-j)\\
Max_{i-n+1< j\le i}(T[j]-j)+i
\]

即我们多出来的需要时间的最大值。

那对于一个局面,我们的答案为:

\[Ans=Min_{n< i\le2*n}(Max_{i-n+1< j\le i}(T[j]-j)+i)
\]

好的,我们已经能在 \(nlogn\) 的时间内求出答案了!

然后继续转式子,用 \(i+(n-1)\) 来代替 \(i\) ,也就是把模型再转化回去:

\[Ans=Min_{1\le i\le n}(Max_{i\le j\le i+n-1}(T[j]-j)+i)+n-1
\]

我们考虑将一个数组 \(num[i]=T[i]-i\) 代入:

\[Ans=Min_{1\le i\le n}(Max_{i\le j\le i+n-1}num[j]+i)+n-1
\]

此时,我们可以扩大一下下标范围,因为 \(num[i+n]=T[i]-(i+n)\le num[i]=T[i]-i\) ,所以,我们不妨改大 \(Max\) 的取值范围:

\[Ans=Min_{1\le i\le n}(Max_{i\le j\le 2*n}num[j]+i)+n-1
\]

由于此时范围已经到了序列末端,那么等价于我们对于每次枚举的 \(i\) ,答案肯定对应为:后缀最大值\(+i+n-1\) 。

表面看起来这并不会使复杂度变优,但是我们注意到一件事情,那就是对于某一段 \(i\) ,它们的后缀最大值肯定都是相等的,而对于某个后缀最大值,它对答案的最优贡献肯定是符合条件的,最小的那个 \(i\) 。

所以我们尝试一下考虑一个相对后缀最大值来讲的答案:

  • \(num[i]\) 本身就是后缀最大值,向左找到第一个大于 \(num[i]\) 的位置 \(j\) ,则对于后缀最大值 \(num[i]\) 而言,它所能贡献的最优答案为 \(num[i]+j+1+n-1=num[i]+j+n\) 。
  • \(num[i]\) 不是后缀最大值,那么它的最优答案等同于后缀最大值位置。

看起来是不是好搞一点了

这启发我们维护一个从后往前单调递增的序列,或者说单调栈,遇到大于栈顶的就压入栈,否则不作处理。

设单调栈内元素为 \(stack_0,stack_1\ ......\ stack_{top}\) 那么对于元素 \(stack_i\) ,它的答案就是 \(num[stack_i]+stack_{i+1}+n\) ,我们只需要取栈内答案最小值即可。

那这个东西怎么维护呢?这就是一个神仙技巧了:线段树维护单调栈!

我也觉得这个东西挺仙的

首先考虑我们维护的是从后往前单调递增的一个栈,那么我们只需要注意合并即可。

合并过程如下 (可能挺难理解的):

  • 对于右区间,如果最大值大于当前栈内最大值,那么左区间相对当前答案就可以直接采用,然后递归处理右区间并加入当前区间
  • 如果右区间最大值小于栈内最大值,递归处理左区间。

确实很难理解 = = 。我们需要维护如下信息:区间最大值,左子区间答案。然后递归统计即可。

而且我们其实不用维护 \(2*n\) 这么长的区间,因为 \(n+1\sim 2*n\) 的最大值肯定为 \(1\sim n\) 内最大值 \(-n\) 嘛,那么右子区间的栈内最大值就是左子区间最大值 \(-n\) 嘛!

其实代码异常的短,只有 \(add\) , \(query\) ,和 \(pushup\) 操作,建议配合代码及注释理解。

Code

  1. #include <cstdio>
  2. #include <iostream>
  3. #define ls (o << 1)
  4. #define rs ((o << 1) + 1)
  5. #define mid ((l + r) >> 1)
  6. using namespace std;
  7. int n, m, p, a[100001];
  8. int L, R, x, y, ans, maxx[400001], adv[400001];
  9. int query(int o, int l, int r) {
  10. if (l == r)
  11. return maxx[o] > x
  12. ? x + l
  13. : 1e9; // x为栈内最大值,l自然就为答案计算式子中的 p(i+1)
  14. return maxx[rs] > x ? min(adv[o], query(rs, mid + 1, r))
  15. : query(ls, l, mid); //判断左右子树最大值贡献
  16. }
  17. void pushup(int o, int l, int r) {
  18. maxx[o] = max(maxx[ls], maxx[rs]); //最大值合并
  19. x = maxx[rs], adv[o] = query(ls, l, mid); //处理左子树答案
  20. }
  21. void add(int o, int l, int r) {
  22. if (l == r) {
  23. maxx[o] = a[l] - l; //赋值为 num 数组
  24. return;
  25. }
  26. if (mid >= L) add(ls, l, mid);
  27. if (mid < R) add(rs, mid + 1, r);
  28. pushup(o, l, r);
  29. }
  30. int main() {
  31. cin >> n >> m >> p;
  32. for (int i = 1; i <= n; i++) {
  33. scanf("%d", &a[i]);
  34. L = i, R = i;
  35. add(1, 1, n);
  36. } //先建树
  37. x = maxx[1] - n;
  38. ans = query(1, 1, n) + n;
  39. printf("%d\n", ans);
  40. while (m--) {
  41. scanf("%d%d", &y, &x);
  42. y ^= (ans * p), x ^= (ans * p);
  43. a[y] = x, L = y, R = y;
  44. add(1, 1, n);
  45. x = maxx[1] - n; // n+1 - 2*n 最大值
  46. printf("%d\n", ans = query(1, 1, n) + n); //计算答案
  47. }
  48. }

【HNOI 2018】转盘的更多相关文章

  1. [HNOI 2018]转盘

    Description 题库链接 在一个环上有 \(n\) 个物品,第 \(i\) 个物品的出现时间为 \(T_i\) .一开始你可以任意选择一个物品的位置作为起始位置,然后以这个位置为起点沿正方向走 ...

  2. [HNOI/AHOI2018]转盘(线段树优化单调)

    gugu  bz lei了lei了,事独流体毒瘤题 一句话题意:任选一个点开始,每个时刻向前走一步或者站着不动 问实现每一个点都在$T_i$之后被访问到的最短时间 Step 1 该题可证: 最优方案必 ...

  3. HNOI 2018 简要题解

    寻宝游戏 毒瘤题. 估计考试只会前30pts30pts30pts暴力然后果断走人. 正解是考虑到一个数&1\&1&1和∣0|0∣0都没有变化,&0\&0& ...

  4. [HNOI 2018]道路

    Description 题库链接 给出一棵含有 \(n\) 个叶子节点的二叉树,对于每个非叶子节点的节点,其与左儿子相连的边为公路,其与右儿子相连的边为铁路.对于每个节点,选择一条与其儿子相连的铁路或 ...

  5. [HNOI 2018]游戏

    Description 题库链接 有 \(n\) 个房间排成一列,编号为 \(1,2,...,n\) ,相邻的房间之间都有一道门.其中 \(m\) 个门上锁,其余的门都能直接打开.现在已知每把锁的钥匙 ...

  6. [HNOI 2018]排列

    Description 题库链接 给定 \(n\) 个整数 \(a_1, a_2, \dots, a_n, 0 \le ai \le n\) ,以及 \(n\) 个整数 \(w_1, w_2, \do ...

  7. 【HNOI 2018】毒瘤

    Problem Description 从前有一名毒瘤. 毒瘤最近发现了量产毒瘤题的奥秘.考虑如下类型的数据结构题:给出一个数组,要求支持若干种奇奇怪怪的修改操作(例如给一个区间内的数同时加上 \(c ...

  8. 【HNOI 2018】排列

    Problem Description 给定 \(n\) 个整数 \(a_1, a_2, \ldots , a_n(0 \le a_i \le n)\),以及 \(n\) 个整数 \(w_1, w_2 ...

  9. 【HNOI 2018】游戏

    Problem Description 一次小 \(G\) 和小 \(H\) 在玩寻宝游戏,有 \(n\) 个房间排成一列,编号为 \(1,2,-,n\),相邻房间之间都有 \(1\) 道门.其中一部 ...

随机推荐

  1. css实现三角形相关

    1.css样式面包屑导航条实现矩形和三角箭头拼接 .cssTest { font-family: PingFangSC-Regular; font-size: 16px; color: #333333 ...

  2. 简单了解request与response

    本文对 request. response 简单描述,未涉及到具体的浏览器缓存.重定向.请求转发等代码部分. 一.Web服务器,浏览器,代理服务器 在看 response.request 对象之前,先 ...

  3. BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序

    BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...

  4. jmeter-PC注册遇到的问题与解决

    2019-04-26问题:

  5. git提交代码时,Unstaged changes如何过滤.class .log等文件

    在项目下创建一个.gitignore文件,内容如下: 可以在文件目录中加入这个文件,也可以在eclipse中项目下加入此文件 /target/表示忽略target文件夹下的内容 .class 表示忽略 ...

  6. 2018-2019-2 《网络对抗技术》Exp0 Kali安装 Week1 20165321

    安装kali 在vm里面新建虚拟机,选择典型 选择安装程序光盘镜像文件,系统出现无法检测此光盘镜像中的操作系统 虚拟机命名选择安装位置 给虚拟机分配的磁盘大小 点击自定义硬件,更改虚拟机硬件 选择Gr ...

  7. C++ 用三元组表示法存储稀疏矩阵

    若有一个矩阵(m*n),其中非0元素个数远少于数值为0的元素个数,若开辟一个m*n大空间,来存储这样一个很多元素值为0的矩阵,浪费空间,于是我们只存储这些非0的元素的下标及数值 用一个结构体——三元组 ...

  8. jq元素拖拽

    <div id="a1"></div> js <script type="text/javascript"> $(funct ...

  9. asp.net 经常用到需要判断文本框是否输入的数字是小数,有无正负,几位小数,可以封装一起判断

    /// <summary> /// 判断是否为小数点数字且带符号 /// </summary> /// <param name="symbol"> ...

  10. 全局路径规划算法Dijkstra(迪杰斯特拉算法)- matlab

    参考博客链接:https://www.cnblogs.com/kex1n/p/4178782.html Dijkstra是常用的全局路径规划算法,其本质上是一个最短路径寻优算法.算法的详细介绍参考上述 ...