[NOIP 2024 模拟12]序列

news/2024/11/14 9:21:47/文章来源:https://www.cnblogs.com/maniubi/p/18542707

[NOIP 2024 模拟12]序列

题意

给出长度为 \(n\) 的序列 \(a\),每次操作给出 \(l,r,k\),把 \([l,r]\) 进行 \(k\) 次循环位移。

每次操作结束后查询整个序列是否存在三元上升子序列,即是否存在 \(i<j<k,a_i<a_j<a_k\)

思路

区间循环位移使用 FHQ-Treap 维护,问题在于如何找答案。

考虑已知左右儿子的信息,如何算出当前点的信息。

有四种情况:

  1. 三元上升子序列的三个元素都在同一个儿子中。
  2. 一个在左儿子,一个在自己,一个在右儿子。
  3. 两个在左儿子,一个在自己或右儿子。
  4. 一个在自己或左儿子,两个在右儿子。

第一种情况直接继承左右儿子的答案。

第二种情况为尽量满足条件,一定选左儿子最小值和右儿子最大值进行统计。

第三种情况为尽量满足条件,一定选择左儿子中结尾最小的二元上升子序列进行统计。

第四种情况为尽量满足条件,一定选择右儿子中开头最大的二元上升子序列进行统计。

综上 ,需要维护的信息如下:

  1. 最小值,最大值
  2. 结尾最小的二元上升子序列
  3. 开头最大的二元上升子序列
  4. 答案

最小值最大值的维护平凡,答案的维护按照上面的讨论判断即可。

重点在于如何维护 2 和 3。

首先继承左右儿子信息,然后呢?

为了查询结尾最小的二元上升子序列,开头一定要最小,

所以要查询右儿子内左儿子最小值的后继,

同理查询开头最大的二元上升子序列,结尾一定要最大,

所以要查询左儿子内右儿子最大值的前驱。

但是这里的 FHQ-Treap 维护的是排名信息,不支持前驱后继的查询,

这里就需要一个事实:

由于只查询全局信息,如果当前点答案已经为 YES,直接不维护即可,

若答案为 NO,有:

  1. 左儿子内小于右儿子最大值的数单调递减,
  2. 右儿子内大于左儿子最小值的数单调递减,

用反证法证明 1:

若不单调递减,必存在 \(i<j,a_i<a_j\),再根据 \(a_i<a_j<mx\)

得出存在三元上升子序列,与条件矛盾,所以结论成立。

2 的证明同理。

有了这两个结论,前驱后继转化为了:

左儿子中小于右儿子最大值且在最左边的数,

右儿子中大于左儿子最小值且在最右边的数。

可以用类似平衡树上二分的思路做。

不要忘了平衡树上自己可以贡献信息,

不能只统计左右儿子的贡献。

时间复杂度:\(O(n\log^2 n)\)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;struct treap {struct node {int ls, rs;int val, siz, key;int mx, mn; // 最大最小值int Mx, Mn; // 开头最大/结尾最小的二元上升子序列bool ans;node() {mx = 0;mn = INT_MAX;}} t[N];int cnt, root;int new_node(int val) { ++ cnt;t[cnt].ls = 0, t[cnt].rs = 0;t[cnt].val = val, t[cnt].siz = 1;t[cnt].key = rand();t[cnt].mx = t[cnt].mn = val;t[cnt].Mx = t[cnt].Mn = -1;t[cnt].ans = 0;return cnt;}int findL(int p, int k) { // 平衡树上二分 查询最左侧小于 k 的数if (t[p].mn >= k) return -1;if (!t[p].ls && !t[p].rs) return t[p].val;if (t[p].ls && t[t[p].ls].mn < k) return findL(t[p].ls, k);if (t[p].val < k) return t[p].val;if (t[p].rs && t[t[p].rs].mn < k) return findL(t[p].rs, k);return -1;}int findR(int p, int k) { // 平衡树上二分 查询最右侧大于 k 的数if (t[p].mx <= k) return -1;if (!t[p].ls && !t[p].rs) return t[p].val;if (t[p].rs && t[t[p].rs].mx > k) return findR(t[p].rs, k);if (t[p].val > k) return t[p].val;if (t[p].ls && t[t[p].ls].mx > k) return findR(t[p].ls, k);return -1;}void push_up(int p) {t[p].siz = t[t[p].ls].siz + t[t[p].rs].siz + 1; t[p].mx = max({t[p].val, t[t[p].ls].mx, t[t[p].rs].mx}); // 最大最小值t[p].mn = min({t[p].val, t[t[p].ls].mn, t[t[p].rs].mn});t[p].ans = t[t[p].ls].ans || t[t[p].rs].ans; // 继承t[p].Mx = t[p].Mn = -1; if (t[p].ls && t[p].rs) { // 维护答案if (~t[t[p].rs].Mx) t[p].ans |= (t[t[p].ls].mn < t[t[p].rs].Mx);if (~t[t[p].ls].Mn) t[p].ans |= (t[t[p].ls].Mn < t[t[p].rs].mx);}if (t[p].ls && (~t[t[p].ls].Mn)) t[p].ans |= (t[t[p].ls].Mn < t[p].val); // 维护答案if (t[p].rs && (~t[t[p].rs].Mx)) t[p].ans |= (t[p].val < t[t[p].rs].Mx); // 维护答案if (t[p].ls && t[p].rs) t[p].ans |= (t[t[p].ls].mn < t[p].val && t[p].val < t[t[p].rs].mx); // 维护答案if (t[p].ans) return ; // 答案为 YES 直接不维护if (t[p].ls) t[p].Mx = findL(t[p].ls, t[p].val); // 自己的贡献if (t[p].ls && t[p].rs) t[p].Mx = max(t[p].Mx, findL(t[p].ls, t[t[p].rs].mx)); // 右儿子的贡献if (t[p].rs && t[p].val < t[t[p].rs].mx) t[p].Mx = max(t[p].Mx, t[p].val); // 自己的贡献if (t[p].rs) t[p].Mn = findR(t[p].rs, t[p].val); // 自己的贡献if (t[p].ls && t[p].rs) t[p].Mn = min((t[p].Mn != -1 ? t[p].Mn : (int)1e9), findR(t[p].rs, t[t[p].ls].mn)); // 左儿子的贡献if (t[p].ls && t[t[p].ls].mn < t[p].val) t[p].Mn = min((t[p].Mn != -1 ? t[p].Mn : (int)1e9), t[p].val); // 自己的贡献if (t[p].ls) t[p].Mx = max(t[p].Mx, t[t[p].ls].Mx); // 继承if (t[p].rs) t[p].Mx = max(t[p].Mx, t[t[p].rs].Mx); // 继承if (t[p].ls && t[t[p].ls].Mn != -1) t[p].Mn = min((t[p].Mn != -1 ? t[p].Mn : (int)1e9), t[t[p].ls].Mn); // 继承if (t[p].rs && t[t[p].rs].Mn != -1) t[p].Mn = min((t[p].Mn != -1 ? t[p].Mn : (int)1e9), t[t[p].rs].Mn); // 继承}void split(int p, int k, int &x, int &y) {if (!p) {x = y = 0;return ;}if (t[t[p].ls].siz + 1 <= k) {x = p;split(t[p].rs, k - t[t[p].ls].siz - 1, t[p].rs, y);} else {y = p;split(t[p].ls, k, x, t[p].ls);}push_up(p);}int merge(int x, int y) {if (!x || !y) return x + y;if (t[x].key > t[y].key) {t[x].rs = merge(t[x].rs, y);push_up(x);return x;} else {t[y].ls = merge(x, t[y].ls);push_up(y);return y;}}void forMove(int l, int r, int k) { // 循环位移int x, y, z;split(root, r, y, z);split(y, l - 1, x, y);int a, b, len = r - l + 1;split(y, len - k, a, b);y = merge(b, a);int temp = merge(x, y);root = merge(temp, z);}void push_back(int val) {root = merge(root, new_node(val));}bool query() {return t[root].ans;}void display(int p) {if (!p) return ;display(t[p].ls);cout << t[p].val << " ";display(t[p].rs);if (p == root) cout << "\n";}
} T;int n, q, a[N];int main() {freopen("xu.in", "r", stdin);freopen("xu.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0); cout.tie(0);cin >> n;for (int i = 1; i <= n; i ++) cin >> a[i];for (int i = 1; i <= n; i ++) T.push_back(a[i]);	cin >> q;while (q --) {int l, r, k;cin >> l >> r >> k;T.forMove(l, r, k);if (T.query()) cout << "YES\n";else cout << "NO\n";}return 0;
}

这道题带来的启发

有时得到想要的东西了就可以摆烂了。

维护信息不一定完全从左右儿子的信息来,

可以先通过左右儿子的信息得到一些信息,

根据这些信息推出性质,方便转移。

有时 push_up 不一定是 \(O(1)\)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/832483.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全局平衡二叉树 (GBST) 小记

全局平衡二叉树 (GBST) 小记 以下全局平衡二叉树简称 \(\text{GBST(Globel Balanced Search Tree)}\)。 我认识的大多数人,对 \(\text{GBST}\) 的理解基本上都是 静态 \(\text{LCT}\),或者静态 \(\text{Top Tree}\),不过我对 \(\text{LCT}\) 的理解可能还差一点,所以我不打…

考研打卡(15)

开局(15) 开始时间 2024-11-12 20:37:51 结束时间 2024-11-12 22:41:32现在在敷泥膜数据结构设已知一稀疏矩阵的三元组表为:(1,2,3),(1,6,1),(3,1,5),(3,2,-1) (5,4,5),(5,1,-3),则其转置矩阵的三元组表中的第三个三元组为____(山东大学2013年) A (2,…

25. 使用MySQL之使用触发器

1. 触发器 MySQL语句在需要时被执行,存储过程也是如此。但是,如果你想要某条语句(或某些语句)在事件发生时自动执行,怎么办呢?例如:每当增加一个顾客到某个数据库表时,都检查其电话号码格式是 否正确,州的缩写是否为大写;每当订购一个产品时,都从库存数量中减去订购…

Mysql表分区实操

创建分区表 create table user(id int(11) not null,name varchar(32) not null) partition by range(id) ( partition p0 values less than(10), partition p1 values less than(20), partition p2 values less than(30), partition p3 values less than maxvalue )分区表数据…

【Azure Bot Service】部署NodeJS ChatBot代码到App Service中无法自动启动

问题描述 把NodeJS的ChatBot代码部署到App Service环境中,通过VS Code直接部署,显示部署成功。但是通过URL访问时候,却是 :( Application Error 。问题解答 App Service遇见Application Error,第一步,查看日志。 发现启动时候遇见: 2024-11-12T12:22:40.366223350Z Error…

Chrome如何卸载掉插件?如此简单!

前言 我之前由于好奇,一下子装了很多的chrome插件,这些插件虽然都有很多功能,但是,由于我的电脑性能不佳,浏览器一下子装这么多插件,有点带不动了。所以,我就想到了卸载谷歌浏览器,哦不,是卸载浏览器里面的插件。今天,我就来介绍下,如何在chrome里面卸载一些不常用的…

rocky9如何重置root密码

rocky9如何重置root密码 第一步第二步 在箭头指的地方把 "ro" 改成 "rw"另一个箭头指的地方写上如下内容:init=/bin/bash如图所示第三步 Ctrl + x 按当前的配置启动第四步 输入命令root 用户密码直接输入以下内容passwd如果更改其他用户第五步 最为关键的…

多校A层冲刺NOIP2024模拟赛21

多校A层冲刺NOIP2024模拟赛21\(T1\) A. 送信卒 \(90pts/100pts\)部分分\(90pts\)设最后的可能的最短路中左右共移动了 \(d\) 次,上下共移动了 \(x\) 次。则等价于求 \(\min \{ x_{i}k+d_{i} \}=s\) 的解,观察到 \(d \in [0,\min(\left\lceil \frac{nm}{2} \right\rceil,s)]\)…

[论文阅读] ZePo: Zero-Shot Portrait Stylization with Faster Sampling

写在前面 原文:ZePo GitHub:Github ZePo 关键词:肖像风格化、扩散模型、零样本快速生成 阅读理由:对扩散模型的改进,可以实现零样本快速生成图像,学习一下思路以及实验设计 前置知识:LCM以及GithubLCM(找时间写一下),可参考LCM&CM,一致性蒸馏、图像质量评价速览…

2024.11.12 鲜花

P11270 【MX-S5-T4】魔法少女们 题解这世界那么多人 这世界有那么多人 人群里 敞着一扇门 我迷朦的眼睛里长存 初见你蓝色清晨 这世界有那么多人 多幸运 我有个我们 这悠长命运中的晨昏 常让我 望远方出神 灰树叶飘转在池塘 看飞机轰的一声去远乡 光阴的长廊 脚步声叫嚷 灯一亮…

Java代码实现行列转换

本代码想要达到的效果 测试完整代码如下(copy直接运行): public class TestConvert { public static void main(String[] args) { ArrayList<Attribute> sourceList = new ArrayList<>(); for (int i = 0; i < 3; i++) { for (i…

SS241112A. 定向越野(walk)

这道题目要求找到从起点出发遍历所有点并回到起点的最小路径长度,路径必须直角拐弯。文章证明了答案是有理数,并通过直观分析和大胆猜测得出初始方向必须是某一个向量的方向,最终使用状压 DP 解决问题,时间复杂度为 $O(n^4 2^n)$。SS241112A. 定向越野(walk) 题意 给你 \…