CF1672I PermutationForces 题解

news/2025/3/26 15:36:14/文章来源:https://www.cnblogs.com/Scarab/p/18788713

Description

给定一个长度为 \(n\) 的排列 \(p_1,p_2,\ldots,p_n\),你可以进行如下操作若干次:

  • 选择 \(1\leq i\leq |p|\) 满足 \(|i-p_i|\leq m\)
  • 对于所有 \(1\leq j\leq |p|\)\(j\),若满足 \(p_i<p_j\),则 \(p_j\leftarrow p_j-1\)
  • 之后删去 \(p_i\)\(p\) 后面的元素向前补位。

求最小的 \(m\) 使经过 \(n\) 次操作能将 \(p\) 删空。

\(1\leq n\leq 5\times 10^5\)

Solution

首先会有一个想法是每次选择 \(|i-p_i|\) 最小的进行操作,这么做我们可能会担心操作顺序的影响。但是可以证明这么做就是对的。

不妨设 \(f_i=|i-p_i|\),且 \(i<p_i\),考虑分讨 \(j\)\(p_j\) 的取值。有如下几种可能:

  1. \(i<j<p_j<p_i\)\(f_j\) 变为 \(f_j+1\)
  2. \(i<p_j<j<p_i\)\(f_j\) 变为 \(f_j-1\)
  3. \(j<i<p_j<p_i\)\(f_j\) 不变。
  4. \(p_j<i<j<p_i\)\(f_j\) 变为 \(f_j-1\)
  5. \(j<i<p_i<p_j\)\(f_j\) 变为 \(f_j-1\)
  6. \(p_j<i<p_i<j\)\(f_j\) 变为 \(f_j-1\)
  7. \(i<j<p_i<p_j\)\(f_j\) 不变。
  8. \(i<p_j<p_i<j\)\(f_j\) 不变。
  9. \(i<p_i<j<p_j\)\(f_j\) 不变。
  10. \(i<p_i<p_j<j\)\(f_j\) 不变。

观察这些情况可以发现对于 \(f_j\leq f_i\) 的所有情况,操作后都不会大于 \(f_i\)。而对于 \(f_j>f_i\) 的情况,操作后 \(f_j\) 都不会变大。

所以每次贪心地选择 \(f_i\) 最小的 \(i\) 进行操作一定不劣。

暴力维护这个东西是 \(O(n\log^2n)\) 或者 \(O(n\sqrt n)\),不太能过。


考虑优化。

容易发现在操作的过程中 \(f_j\) 的正负性一定不会改变,因为在 \(f_j=0\) 的时候才会变,但这时一定是能选择 \(f_i=0\) 的操作,所以操作完 \(j\)\(a_j\) 的变化量一定相同,\(f_j\) 也就不变了。

所以我们对于 \(i\leq a_i\)\(i>a_i\) 单独处理。

对于 \(i\leq a_i\) 的情况。将 \((i,a_i)\) 看成坐标系上的点,那么如果存在 \(j>i,a_j<a_i\),即 \(f_i\)\(j\) 删掉之前一定不会比 \(f_j\) 更优。所以 \(f_i\) 可能成为答案当且仅当 \((i,a_i)\) 右下方没有点。

考虑只把可能成为答案的点拿出来,那么这些点都是 \(a_i\) 的后缀最小值,且构成一个从左下到右上的递增点列。

由于这个具有单调性,所以用线段树维护这个递增的点列上每个点的答案。

每次删掉一个点后还要更新后缀最小值,容易发现每次只需要暴力在前驱和后继之间找最小值,然后递归即可。

显然每个点在加入之后不会再被删除。

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

Code

#include <bits/stdc++.h>// #define int int64_tusing pii = std::pair<int, int>;const int kMaxN = 5e5 + 5;int n, ans;
int a[kMaxN];
std::set<int> st1, st2;
std::set<pii> st3, st4;struct BIT {int c[kMaxN];void upd(int x, int v) {for (; x <= n; x += x & -x) c[x] += v;}int qry(int x) {int ret = 0;for (; x; x -= x & -x) ret += c[x];return ret;}
} bit1, bit2;struct SGT {int N;pii mi[kMaxN * 4];void pushup(int x) {mi[x] = std::min(mi[x << 1], mi[x << 1 | 1]);}void build(int n) {for (N = 1; N <= n + 1; N <<= 1) {}std::fill_n(mi, 2 * N, pii{1e9, 0});}void update(int x, pii v) {mi[x += N] = v;for (x >>= 1; x; x >>= 1) pushup(x);}pii query(int l, int r) {if (l > r) return {1e9, 0};pii ret = {1e9, 0};for (l += N - 1, r += N + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {if (~l & 1) ret = std::min(ret, mi[l ^ 1]);if (r & 1) ret = std::min(ret, mi[r ^ 1]);}return ret;}
} sgt1, sgt2;struct SGT_s {pii mi[kMaxN * 4];int tag[kMaxN * 4];void pushup(int x) {mi[x] = std::min(mi[x << 1], mi[x << 1 | 1]);}void addtag(int x, int v) {mi[x].first += v, tag[x] += v;}void pushdown(int x) {if (tag[x]) {addtag(x << 1, tag[x]), addtag(x << 1 | 1, tag[x]);tag[x] = 0;}}void build(int x, int l, int r) {if (l == r) return void(mi[x] = {1e9, l});int mid = (l + r) >> 1;build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);pushup(x);}void update1(int x, int l, int r, int ql, int v) {if (l == r) return void(mi[x].first = v);pushdown(x);int mid = (l + r) >> 1;if (ql <= mid) update1(x << 1, l, mid, ql, v);else update1(x << 1 | 1, mid + 1, r, ql, v);pushup(x);}void update2(int x, int l, int r, int ql, int qr, int v) {if (l > qr || r < ql) return;else if (l >= ql && r <= qr) return addtag(x, v);pushdown(x);int mid = (l + r) >> 1;update2(x << 1, l, mid, ql, qr, v), update2(x << 1 | 1, mid + 1, r, ql, qr, v);pushup(x);}
} sgt3, sgt4;void rebuild1(int l, int r, int suf) {for (; l <= r;) {auto p = sgt1.query(l, r);// if (p.second) assert(p.first == a[p.second]);if (!p.second || p.first > suf) return;int x = p.second;st1.emplace(x), st3.emplace(a[x], x);sgt3.update1(1, 1, n, x, abs((x - bit1.qry(x)) - (a[x] - bit2.qry(a[x]))));l = x + 1;}
}void rebuild2(int l, int r, int pre) {for (; l <= r;) {auto p = sgt2.query(l, r);// if (p.second) assert(p.first == -a[p.second]);if (!p.second || a[p.second] < pre) return;int x = p.second;st2.emplace(x), st4.emplace(a[x], x);sgt4.update1(1, 1, n, x, abs((x - bit1.qry(x)) - (a[x] - bit2.qry(a[x]))));r = x - 1;}
}void upd(int x) {sgt3.update2(1, 1, n, x, n, 1), sgt4.update2(1, 1, n, x, n, -1);auto it1 = st3.lower_bound({a[x], x}), it2 = st4.lower_bound({a[x], x});if (it1 != st3.end()) sgt3.update2(1, 1, n, it1->second, n, -1);if (it2 != st4.end()) sgt4.update2(1, 1, n, it2->second, n, 1);bit1.upd(x, 1), bit2.upd(a[x], 1);
}void work(int x) {ans = std::max(ans, abs((x - bit1.qry(x)) - (a[x] - bit2.qry(a[x]))));if (x <= a[x]) {sgt1.update(x, {1e9, 0});st1.erase(x), st3.erase({a[x], x});sgt3.update1(1, 1, n, x, 1e9);auto it = st1.lower_bound(x);int nxt = *it, pre = *--it;rebuild1(pre + 1, nxt - 1, nxt == n + 1 ? (int)1e9 : a[nxt]);upd(x);} else {sgt2.update(x, {1e9, 0});st2.erase(x), st4.erase({a[x], x});sgt4.update1(1, 1, n, x, 1e9);auto it = st2.lower_bound(x);int nxt = *it, pre = *--it;rebuild2(pre + 1, nxt - 1, a[pre]);upd(x);}// std::cerr << x << ' ' << ans << ' ' << st3.size() << ' ' << st4.size() << '\n';
}void prework() {st1.emplace(0), st1.emplace(n + 1);st2.emplace(0), st2.emplace(n + 1);sgt1.build(n), sgt2.build(n);sgt3.build(1, 1, n), sgt4.build(1, 1, n);for (int i = 1; i <= n; ++i) {if (i <= a[i]) sgt1.update(i, {a[i], i});else sgt2.update(i, {-a[i], i});}rebuild1(1, n, 1e9), rebuild2(1, n, 0);
}void dickdreamer() {std::cin >> n;for (int i = 1; i <= n; ++i) std::cin >> a[i];prework();for (int i = 1; i <= n; ++i) {work(std::min(sgt3.mi[1], sgt4.mi[1]).second);}std::cout << ans << '\n';
}int32_t main() {
#ifdef ORZXKRfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifstd::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);int T = 1;// std::cin >> T;while (T--) dickdreamer();// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";return 0;
}

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

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

相关文章

使用LattePanda V1制作了一个简单的基于batocera的游戏仿真站

现在,我们正在使用我们最近开发的Batocera arcade Box使用PS1模拟器运行Roadrash。我们使用的是Batocera Linux,这是一个开源且完全免费的复古游戏发行版本,旨在将任何计算机或单板计算机变成游戏控制台。 在这里,我们在相当旧的Latte Panda V1单板计算机上启动了Batocera,…

河北省科级政策app 连接MySQL数据库

项目结构 在连接数据库的时候要把localhost改为自己电脑的IP地址 开放mysql数据库远程访问的权限 使用jdbc的方法把jar包导入项目中

【Guava工具类】StringsInts

String相关工具 Strings Guava 提供了一系列用于字符串处理的工具: 对字符串为null或空的处理nullToEmpty(@Nullable String string):如果非空,则返回给定的字符串;否则返回空字符串 public static String nullToEmpty(@Nullable String string) {//如果string为null则返回…

我开发的【巨大娘的玩耍♥】游戏正在众筹中,参与即可获得限量优惠回报

大家好,我开发的【巨大娘的玩耍♥】游戏正在众筹中,参与即可获得限量优惠回报 游戏目前正在爱发电上众筹,众筹总额达成5千元即可开发和发布正式版。众筹截止日期:2025年7月23日 支持者可获得 8折激活码、获得所有玩家可见的特殊成就、上感谢名单 等 限量 回报~ 点我查看具体…

用于遥控车的先进PCB

该定制板集成了大功率电机控制,无线通信和高效电源管理在RC汽车的世界里,精确控制和效率是至关重要的。为了推动性能的极限,我专门为RC汽车设计了一个先进的PCB。这种定制板集成了大功率电机控制,无线通信和高效的电源管理,使其成为爱好者和机器人爱好者的理想选择。 为了…

读DAMA数据管理知识体系指南29文件和内容管理活动

读DAMA数据管理知识体系指南29文件和内容管理活动1. 规划生命周期的管理 1.1. 从文件的创建或接收文件后的分发、存储、检索、归档和潜在的销毁 1.2. 规划包括开发分类/索引系统和分类法,以实现文件的存储和检索 1.3. 重要的是,生命周期规划中需要为档案建立具体的制度 1.4. …

c语言实验2

1 #include <stdio.h>2 #include <stdlib.h>3 #include <time.h>4 5 #define N 56 7 int main() {8 int number;9 int i; 10 11 srand(time(0)); // 以当前系统时间作为随机种子 12 for(i = 0; i < N; ++i) { 13 number = r…

使用 Browser-Use WebUI + DeepSeek 实现浏览器AI自动化全攻略

使用 Browser-Use WebUI + DeepSeek 实现浏览器AI自动化全攻略 环境准备 1. 安装 Python 环境版本要求:Python 3.11 或更高版本 验证安装:命令行执行 python --version 注意:安装时需勾选 "Add to PATH" 选项(Windows用户)2. 核心工具安装 # 安装 browser-use 框…

20234214 2024-2025-2 《Python程序设计》实验一报告

20234214 2024-2025-2 《Python程序设计》实验一报告 课程:《Python程序设计》 班级: 2342 姓名: 唐果儿 学号:20234214 实验教师:王志强 实验日期:2025年3月18日 必修/选修: 公选课 1.实验内容 (一)实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能; …

WinForm 使用 Win32 API 实现的无边框窗口

WinForm 使用 Win32 API 实现的无边框窗口前言 时光荏苒,转眼已近是2025年了。不知不觉两年多没有研究代码了,在这期间 .NET 10 都快 RC 了,前几天刷手机看到张队公众号里有关于 .NET 9.0 AOT 发布的内容,所以写了这些代码来测试一下 AOT 编译的效果,并评估未来是否开发支…

C语言打卡学习第4天(2025.3.23)

今天只写了几道基础题,又看了下数组和冒泡排序,概念搞懂了但是写代码还是比较困难,准备明天把排序这类题好好看看。

一文速通Python并行计算:01 Python多线程编程-基本概念、切换流程、GIL锁机制和生产者与消费者模型

多线程允许程序同时执行多个任务,提升效率和响应性。线程分为新建、就绪、运行、阻塞和死亡五种状态。Python的GIL锁限制多线程并行执行,适合I/O密集型任务。生产者-消费者模型通过共享缓冲区和条件变量实现线程协作,解决数据共享问题。一文速通 Python 并行计算:01 Python…