UVA11990 ``Dynamic‘‘ Inversion

UVA11990 ``Dynamic'' Inversion

  • 题目链接
  • 题意
    • 输入格式
    • 输出格式
  • 分析
    • CDQ分治
    • 嵌套(树状数组套BST)
    • 分块
    • k-D Tree

题目链接

  UVA11990 ``Dynamic’’ Inversion

题意

  给一个 1~n 的排列A,要求按照某种顺序删除一些数(其他数顺序不变),输出每次删除之前逆序对的数目。所谓逆序对数,就是满足i<j 且A[i]>A[j]的有序对(i,j)的数目。

输入格式

  输入包含多组数据,每组数据的第一行为两个整数n 和m(1≤n≤200 000,1≤m≤100 000);接下来的n 行表示初始排列;接下来的m 行按顺序给出删除的整数,每个整数保证不会删除两次。输入结束标志为文件结束符(EOF)。输入文件大小不超过5MB。

输出格式

  对于每次删除,输出删除之前的逆序对数。

分析

  核心思想是先求出初始逆序对个数,然后减去每次删除元素后受影响的逆序对个数。初始逆序对个数利用树状数组可以求出。作为嵌套和分块数据结构的经典题目,下面从CDQ分治、嵌套(本题是树状数组套BST)、分块、k-D Tree这四种常见解法分别做分析。

CDQ分治

  考虑给每个元素三个属性 t,p,vt,p,vt,p,v,分别表示这个元素的删除时间 ttt,位置 ppp,值 vvv,那删除元素 iii 受影响的逆序对 (i,j)(i,j)(i,j) 需要满足以下条件 (ti<tj∧pi<pj∧vi>vj)∨(ti<tj∧pi>pj∧vi<vj)(t_i<t_j \wedge p_i<p_j \wedge v_i > v_j)\vee(t_i<t_j \wedge p_i>p_j \wedge v_i < v_j)(ti<tjpi<pjvi>vj)(ti<tjpi>pjvi<vj),套用CDQ分治处理三维偏序问题的思想即可解决:排序处理第一维 tit_iti,分治处理第二维 pip_ipi,树状数组处理第三维 viv_ivi

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;#define N 200100
struct {int t, p, v;} e[N]; int a[N], p[N], c[N], f[N], m, n; long long s;bool cmp(int i, int j) {return e[i].t < e[j].t;
}void solve(int s, int t) {if (t - s == 1) return;int m = (s+t) / 2, h = m, q = t-1;solve(s, m); solve(m, t);for (int i=s; i<m; ++i) {while (h < t && e[a[h]].p < e[a[i]].p) for (int x=e[a[h++]].v; x; x -= x&-x) ++c[x];if (e[a[i]].t < ::m) for (int x=e[a[i]].v+1, j=e[a[i]].t; x<=n; x += x&-x) f[j] += c[x];}for (int i=m; i<h; ++i) for (int x=e[a[i]].v; x; x -= x&-x) --c[x];for (int i=m-1; i>=s; --i) {while (q >=m && e[a[q]].p > e[a[i]].p) for (int x=e[a[q--]].v; x<=n; x += x&-x) ++c[x];if (e[a[i]].t < ::m) for (int x=e[a[i]].v-1, j=e[a[i]].t; x; x -= x&-x) f[j] += c[x];}if (s == 0 && t == n) return;for (int i=t-1; i>q; --i) for (int x=e[a[i]].v; x<=n; x += x&-x) --c[x];for (int i=s; i<t; ++i) p[i] = a[i];for (int i=s, j=m, k=s; k<t; ++k) a[k] = i == m || (j < t && e[p[j]].p < e[p[i]].p) ? p[j++] : p[i++];
}void solve() {memset(c, s = 0, sizeof(c));for (int i=0; i<n; ++i) {cin >> e[i].v; e[i].p = p[e[i].v] = i; e[i].t = m; a[i] = i;for (int x = e[i].v+1; x<=n; x += x&-x) s += c[x];for (int x = e[i].v; x; x -= x&-x) ++c[x];}for (int i=0, v; i<m; ++i) cin >> v, f[e[p[v]].t = i] = 0;sort(a, a+n, cmp); memset(c, 0, sizeof(c)); solve(0, n);for (int i=0; i<m; s -= f[i++]) cout << s << endl;
}int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);while (cin >> n >> m) solve();return 0;
}

嵌套(树状数组套BST)

  嵌套的解题思路引用自《训练指南》410页:

  令 larger(p,v)larger(p,v)larger(p,v) 表示 Ap>vA_p>vAp>v 是否成立(1 表示成立,0 表示不成立),则“前 k 个数有多少个比 v 大”等于 larger(1,v)+larger(2,v)+larger(3,v)+…+larger(k,v)。
  注意,这是一个前缀查询,因此可以用 Fenwick 树维护。回忆第 3 章中对 Fenwick 树的 C 数组的定义Ci=Ai−lowbit(i)+1+Ai−lowbit(i)+2+…+AiC_i=A_{i-lowbit(i)+1}+A_{i-lowbit(i)+2}+…+A_iCi=Ailowbit(i)+1+Ailowbit(i)+2++Ai  对应到本题中,CiC_iCi 的值实际上等于 “A(i−lowbit(i)+1),…,AiA_(i-lowbit(i)+1), …, A_iA(ilowbit(i)+1),,Ai 中有多少个元素比 v 大”。考虑到本题还有删除操作,可以用第3 章介绍过的名次树来实现这些 CiC_iCi。这样,我们得到了一个 “Fenwick 树套名次树”的特殊数据结构,在O(log2n)O(log^2n)O(log2n)时间内解决了本题。
  具体来说,对于每个 iii,我们用 Ai−lowbit(i)+1,…,AiA_{i-lowbit(i)+1},…, A_iAilowbit(i)+1,,Ai 这些数构造第 iii 棵名次树,支持两个操作:删除元素和统计树里有多少个元素小于某个 kkk。当删除第 iii 个位置的数v 时,按照 Fenwick 树算法计算 Q(i−1)+…+Q(i−lowbit(i−1))…Q(i-1)+…+Q(i-lowbit(i-1))…Q(i1)++Q(ilowbit(i1))这里的 Q(i)Q(i)Q(i) 就是查找第 iii 棵BST 中大于 vvv 的个数。
  这里的名次树并不需要用 Treap 或者伸展树实现,因为它只有删除没有插入和修改。如果给每个结点添加一个“是否已删除”的标记,就可以在不改变树结构的情况下支持删除操作。换句话说,只要在预处理时把每棵名次树都建成完全平衡的二叉树,则两个操作的时间复杂度总是对数级别的。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;#define N 200100
int a[N], p[N], c[N], e[N], m, n, t; long long s;
struct node {int v, d; node *l, *r;} q[N*18], *h[N];node* build(int a, int b) {if (a >= b) return NULL;node *r = q + t++; int m = (a+b)>>1; *r = {e[m], 0, build(a, m), build(m+1, b)};return r;
}int query(const node *p, int s, int v) {if (!p) return 0;if (p->v >= v) return query(p->l, s>>1, v);int ld = (p->l ? p->l->d : 0), c = p->d - ld - (p->r ? p->r->d : 0);return (s>>1) - ld + 1-c + query(p->r, s-1 - (s>>1), v);
}int query(int x) {int a = x-1, b = 0, e = 0;for (int i=x; i; i -= i&-i) a -= c[i];for (int i=p[x]-1; i; i -= i&-i) b += (i&-i) - h[i]->d, e += query(h[i], i&-i, x);return b-e + a-e;
}void remove(node *p, int v) {p->d ++;if (p->l && p->v > v) remove(p->l, v);if (p->r && v > p->v) remove(p->r, v);
}void solve() {memset(c, s = t = 0, sizeof(c));for (int i=1; i<=n; ++i) {cin >> a[i]; p[a[i]] = i;for (int x = a[i]+1; x<=n; x += x&-x) s += c[x];for (int x = a[i]; x; x -= x&-x) ++c[x];int cc = i&-i;for (int j=i-cc+1, k=0; j<=i; ++j) e[k++] = a[j];sort(e, e+cc);h[i] = q + t++; *h[i] = {e[cc>>1], 0, build(0, cc>>1), build((cc>>1)+1, cc)};}memset(c, 0, sizeof(c));while (m--) {int x; cin >> x;cout << s << endl;s -= query(x);for (int i=x; i<=n; i += i&-i) ++c[i];for (int i=p[x]; i<=n; i += i&-i) remove(h[i], x);}
}int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);while (cin >> n >> m) solve();return 0;
}

分块

  设将原序列分成长度为 sss 的块,对于每个块维护一个有序数组。每次删除一个数,容易想到在它前面比它大的数和在它后面比它小的数的计数会被清除,所以对于每个块二分查找个数并相加,对删除的数所在的块暴力统计即可。删除时,直接在原数组中将其标记,有序数组中找到它并将其移至所在块最后方。
  单次查询的时间复杂度为 O(nslog⁡s+s)O(\frac n s \log s + s)O(snlogs+s),单次删除的时间复杂度为 O(s+log⁡s)O(s+\log s)O(s+logs),因此单次查询及删除总的时间复杂度为O(nslog⁡s+s)O(\frac n s \log s + s)O(snlogs+s)
  本题限时3秒,分块的大小s将成为卡常限制。如果取 s=ns=\sqrt ns=n 则总的查询和删除的时间复杂度为O(mnlog⁡n)O(m\sqrt n \log \sqrt n)O(mnlogn),提交容易超时;取 s=nms=\frac n {\sqrt m}s=mn 整体来说时间复杂度O(mmlog⁡nm+nm)O(m\sqrt m \log \frac n {\sqrt m} + n\sqrt m)O(mmlogmn+nm)更优(因为1≤n≤200 000,1≤m≤100 000,m≤n)。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;#define N 200100
#define M 634
int b[M][M], a[N], p[N], c[N], t[M], m, n, y, z; long long s; bool f[N];inline int query(int v) {int s = 0, h = p[v] / z; f[p[v]] = true;for (int i=0; i<h; ++i) s += t[i] - (upper_bound(b[i], b[i]+t[i], v) - b[i]);for (int i=h+1; i<y; ++i) s += lower_bound(b[i], b[i]+t[i], v) - b[i];for (int i=z*h; i<p[v]; ++i) if (a[i] > v && !f[i]) ++s;for (int i=p[v]+1, j=min(z*h+z, n); i<j; ++i) if (a[i] < v && !f[i]) ++s;for (int i=lower_bound(b[h], b[h]+t[h], v) - b[h], j=--t[h]; i<j; ++i) b[h][i] = b[h][i+1];return s;
}void solve() {memset(c, s = 0, sizeof(c)); z = n/sqrt(m)+1; y = (n + z-1) / z;for (int i=0; i<n; ++i) {cin >> a[i]; f[p[a[i]] = i] = false; b[i/z][i%z] = a[i];for (int x = a[i]+1; x<=n; x += x&-x) s += c[x];for (int x = a[i]; x; x -= x&-x) ++c[x];}for (int i=0; i<y; ++i) t[i] = i+1 < y ? z : n - (y-1)*z, sort(b[i], b[i] + t[i]);while (m--) {cout << s << endl;int v; cin >> v;if (m == 0) return;s -= query(v);}
}int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);while (cin >> n >> m) solve();return 0;
}

k-D Tree

  待补充

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

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

相关文章

银河麒麟“安装器”安装方法

书接上回&#xff1a;银河麒麟安装软件商店方法-CSDN博客 过了几天发现当时一不小心把系统自带的“安装器”软件也卸载掉了&#xff0c;导致现在deb文件只能通过命令行安装&#xff0c;寻思这可不行&#xff0c;就想一下应该怎么安装。 首先&#xff0c;为了确认一下安装器的…

计算机毕设分享-基于SpringBoot的健身房管理系统(开题报告+前后端源码+Lun文+开发文档+数据库设计文档)

基于SpringBoot的健身房管理系统分享一套完整的基于SpringBoot的健身房管理系统毕业设计&#xff08;开题报告完整前后端源码Lun文 开发文档数据库设计文档&#xff09;系统分为三个角色功能如下&#xff1a;用户功能需求描述管理员功能需求描述教练功能需求描述开题报告系统功…

代码审计与web安全选择题1

软件供应链安全的基础是&#xff08; &#xff09;A.完善的需求分析B.源代码安全C.渗透测试D.软件测试参考答案&#xff1a;B保证源代码安全的主要措施包括&#xff08; &#xff09;A.开发工具和环境的安全B.代码安全C.渗透测试D.代码审计E.软件的说明文档完整参考…

python基本数据类型 数据类型转换 数字 菜鸟教程笔记

python基本数据类型 数据类型转换 数字 菜鸟教程笔记 1.基本数据类型 Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。 在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"…

USRP X410 X440 5G及未来通信技术的非地面网络(NTN)

概述 在本白皮书中&#xff0c;我们将介绍NTN的现状、正处于探索阶段的一些新应用&#xff0c;以及最重要的一点&#xff0c;我们需要克服哪些技术挑战才能让这个市场充满活力。最后&#xff0c;我们将概述为实现实用高效的测试&#xff0c;NI围绕NTN所做的努力&#xff0c;该测…

基于SpringBoot+Vue的电脑维修管理系统(WebSocket实时聊天、Echarts图形化分析)

“ &#x1f388;系统亮点&#xff1a;WebSocket实时聊天、Echarts图形化分析”01系统开发工具与环境搭建—前后端分离架构项目架构&#xff1a;B/S架构运行环境&#xff1a;win10/win11、jdk17小程序端&#xff1a;技术&#xff1a;Uniapp&#xff1b;UI库&#xff1a;colorUI…

2025.7.28总结

今天真有点小烦&#xff0c;工作有些不太顺利&#xff0c;我是真没想到&#xff0c;阻塞我工作开展得竟然是我的主管。当初需求澄清的时候&#xff0c;开发说要申请一个便携&#xff0c;我当时申请的时候也跟主管说了&#xff0c;需求测试的时候要使用到&#xff0c;但主管要我…

DBA常用数据库查询语句

1 数据库信息 1.1 数据库概要 select a.name "DB Name",e.global_name "Global Name",c.host_name "Host Name",c.instance_name "Instance Name" ,DECODE(c.logins,RESTRICTED,YES,NO) "Restricted Mode",a.log_mode &quo…

【c++深入系列】:万字详解priority_queue(附模拟实现的源码)

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 真正的强大&#xff0c;不是从不跌倒&#xff0c;而是每次跌倒后都能笑着站起来 ★★★ 本文前置知识&#xff1a; 模版 引入 那么pri…

分享一个脚本,从mysql导出数据csv到hdfs临时目录

想从mysql导出一个表到csv文件&#xff0c;然后上传到hdfs&#xff0c;开始使用sqoop&#xff0c;结果各种问题频出&#xff1a; https://blog.csdn.net/weixin_45357522/article/details/149498030 https://blog.csdn.net/weixin_45357522/article/details/149449413 特别是那…

OpenLayers 综合案例-区域掩膜

看过的知识不等于学会。唯有用心总结、系统记录&#xff0c;并通过温故知新反复实践&#xff0c;才能真正掌握一二 作为一名摸爬滚打三年的前端开发&#xff0c;开源社区给了我饭碗&#xff0c;我也将所学的知识体系回馈给大家&#xff0c;助你少走弯路&#xff01; OpenLayers…

30天打牢数模基础-神经网络基础讲解

一、代码说明本代码基于模拟房价数据集&#xff0c;使用scikit-learn库中的MLPRegressor&#xff08;多层感知器回归&#xff09;实现神经网络模型&#xff0c;解决房价预测问题。代码逻辑清晰&#xff0c;适合数模小白入门&#xff0c;包含数据预处理、模型构建、训练评估、新…

Linux应用开发基础知识——LInux学习FreeType编程(七)

目录 一、使用freetype 显示一个文字 二、使用 freetype 显示一行文字 1. 了解笛卡尔坐标系 2. 每个字符的大小可能不同 3. 怎么在指定位置显示一行文字 4. freetype 的几个重要数据结构 4.1、FT_Library结构体 4.2、FT_Face结构体 4.3、FT_GlyphSlot结构体 4.4、FT_G…

Kotlin中Flow

Kotlin Flow 深度解析&#xff1a;从原理到实战一、Flow 核心概念体系1. Flow 的本质与架构Flow 是 Kotlin 协程库中的异步数据流处理框架&#xff0c;核心特点&#xff1a;响应式编程&#xff1a;基于观察者模式的数据处理协程集成&#xff1a;无缝融入 Kotlin 协程生态背压支…

Java程序员学从0学AI(七)

一、前言 上一篇文章围绕 Spring AI 的 Chat Memory&#xff08;聊天记忆&#xff09;功能展开&#xff0c;先是通过代码演示了不使用 Chat Memory 时&#xff0c;大模型因无状态无法记住上下文&#xff08;如用户姓名&#xff09;的情况&#xff0c;随后展示了使用基于内存的 …

ESP32S3 防猫逃脱监测系统

在办公室里&#xff0c;两只可爱的猫咪给大家带来了不少欢乐&#xff0c;但其中一只总爱趁人不注意溜出房间&#xff0c;有时下班后还会被邻居告知它被锁在了外面。为了解决这个问题&#xff0c;我开发了一个基于 SeeedStudio XIAO ESP32S3 Sense 的猫咪逃脱监测预警系统&#…

Python|OpenCV-实现快速处理图像的方法(23)

前言 本文是该专栏的第25篇,后面将持续分享OpenCV计算机视觉的干货知识,记得关注。 在视觉算法落地流程中,数据预处理往往占用 60 % 以上的工程时间。以某沿海城市智慧旅游项目为例,我们从无人机录制的 4K 海滩视频中抽帧得到 10 000 张 PNG 原图,分辨率 38402160,单张体…

Redis四种GetShell方式完整教程

Redis作为高性能内存数据库&#xff0c;若未正确配置认证和访问控制&#xff0c;可能被攻击者利用实现远程代码执行&#xff08;GetShell&#xff09;。本文详细讲解四种常见的Redis GetShell方式&#xff0c;涵盖原理、操作步骤及防御建议。方式一&#xff1a;直接写入Shell脚…

clock_nanosleep系统调用及示例

41. clock_nanosleep - 高精度睡眠 函数介绍 clock_nanosleep系统调用提供纳秒级精度的睡眠功能&#xff0c;支持绝对时间和相对时间两种模式&#xff0c;比传统的nanosleep更加灵活。 函数原型 #include <time.h>int clock_nanosleep(clockid_t clock_id, int flags,con…

用了Flutter包体积增大就弃用Flutter吗?包体积与开发效率,这两者之间如何权衡?

是否因包体积增大而弃用 Flutter&#xff0c;本质上是 “短期成本&#xff08;包体积&#xff09;” 与 “长期价值&#xff08;跨平台效率、体验一致性等&#xff09;” 的权衡 。这一决策没有绝对答案&#xff0c;需结合项目阶段、用户群体、业务需求等具体场景分析。以下从核…