【Ynoi 2019 模拟赛】Yuno loves sqrt technology II

news/2024/9/22 19:29:21

Luogu P5047 Yuno loves sqrt technology II

题意

给定一个长度为 \(n\) 的序列 \(a\)

\(m\) 次询问:查询区间 \([l,r]\) 中的逆序对数。

数据范围与约定

\(1 \le n,m \le 10^5\)\(0 \le a_i \le 10^9\)

题解

看到区间问题,先思考线段树。发现用线段树没法合并两个区间的信息。所以考虑分块。分块确实能做,但是这题卡空间,把分块给 ban 了。于是考虑能不能把询问差分成 \([1,r]-[1,l-1]\) 的形式。发现也拆不开。 那就考虑莫队。

莫队直接做很容易,可以用 \(O(n\sqrt{m}logn)\) 的时间复杂度解决。太慢了,而且莫队不像分块,不能把这个 \(logn\) 放到根号内。

while (query[i].l < l) 为例。假设移动前区间为 \([l,r]\),移动后区间为 \([l',r]\),其中 \(l'=l-1\)

左指针向左移动,区间被扩大了,多增加了一个元素 \(a_{l'}\)。其对当前询问产生的贡献为 \([l,r]\) 中小于 \(a_{l'}\) 的数的个数。

普通的莫队,这里会维护一个全局的平衡树或者树状数组或者别的数据结构,用 \(O(logn)\) 的时间复杂度内求出这个贡献。这是没办法继续优化的,所以考虑这个地方还有没有别的手段。

为了方便,我们令 \(cnt_{[l,r],lower,x}\) 表示区间 \([l,r]\) 中小于 \(x\) 的数的个数。

差分后发现,\(cnt_{[l,r],lower,a_{l'}}=cnt_{[l,n],lower,a_{l'}}-cnt_{[r+1,n],lower,a_{l'}}\)

并且其中 \(cnt_{[l,n],lower,a_{l'}}=cnt_{[l',n],lower,a_{l'}}\)。因为要求的是小于,所以自己并不会对自己做出贡献。

所以代入进去就变成:\(cnt_{[l,r],lower,a_{l'}}=cnt_{[l',n],lower,a_{l'}}-cnt_{[r+1,n],lower,a_{l'}}\)

观察这个式子,我们发现,\(cnt_{[l',n],lower,a_{l'}}\) 非常的好求,从后往前扫一遍预处理一下就行了,复杂度 \(O(nlogn)\),每次查询都可以 \(O(1)\) 回答。

但是 \(cnt_{[r+1,n],lower,a_{l'}}\) 不太好求,考虑到莫队有 \(O(n\sqrt{m})\) 次指针移动的过程,所以对应的这个查询也有 \(O(n\sqrt{m})\) 次。所以我们必须要有一种能够 \(O(1)\) 回答这个询问的办法。

考虑莫队二次离线,把莫队指针移动产生的 \(O(n\sqrt{m})\) 个形如 \(cnt_{[r+1,n],lower,a_{l'}}\) 的询问再次离线下来。这里需要注意一下,lxl 比较毒瘤,卡了线性空间。所以我们需要把 \(O(n\sqrt{m})\) 个询问想办法用 \(O(n+m)\) 的空间存储。发现我们的指针一次移动是单向的,形成了一段区间,所以没必要把所有指针移动都存下来,只需要存移动的起点和终点就可以了。

发现,虽然有 \(O(n\sqrt{m})\) 次查询,但就只有 \(O(n)\) 次插入操作。这里用到了一个叫做“根号平衡”的思想。我们只需要一种能够 \(O(\sqrt{n})\) 插入,\(O(1)\) 查询的数据结构就可以了。

值域分块,查询比一个数小的数的个数。其实就是求区间和。求区间和就想到了前缀和,插入一个数其实就是对若干个前缀和做贡献。

分成块上前缀和和块内前缀和两部分,因为块长是根号,所以暴力对两部分做贡献加一就可以。一次插入复杂度 \(O(\sqrt{n})\)

于是我们就求出了中间所有需要的数据。把他们再组织起来就可以了。

考虑到,莫队中,每次指针移动,都是产生了一个对当前询问的贡献,也就是 \(delta\)。所以我们只需要把每次的 \(delta\) 累加到当前询问下就可以了。而相邻两次询问,也是基于上一次询问的数据,加上了这一次询问指针产生的若干 \(delta\) 贡献。所以对所有的询问再做一次前缀和就行。

最后把莫队排完序后的询问还原成原询问顺序输出就可以。

代码

#include<bits/stdc++.h>
using namespace std;
/*====================*/
#define endl "\n"
/*====================*/
typedef long long lnt;
/*====================*/
const int N = 1e5 + 10;
const int M = 1e5 + 10;
/*====================*/
const int INF = 0X3F3F3F3F;
/*====================*/
int n, m, a[N];
/*====================*/
template<class Type>
class C_FenwickTree
{
private:int n; vector<int>tree;/*====================*/int lowbit(int x) { return x & (-x); }
public:void Init(int n){this->n = n; tree.assign(n + 1, Type());}void Add(int pos, Type d){while (pos <= n){tree[pos] += d;pos += lowbit(pos);}}Type Ask(int pos){Type res = Type();while (pos){res += tree[pos];pos -= lowbit(pos);}return res;}Type Ask(int l, int r){if (l > r)return Type();Type res = Type(); l--;while (r > l)res += tree[r], r -= lowbit(r);while (l > r)res -= tree[l], l -= lowbit(l);return res;}
};
/*====================*/
int preupper[N], suflower[N];
/*====================*/
const int MO_S = 300;
/*====================*/
struct Query1
{int l, r, idx;Query1(int _l = 0, int _r = 0, int _idx = 0){l = _l, r = _r, idx = _idx;}friend bool operator<(const Query1& a, const Query1& b){return (a.l / MO_S == b.l / MO_S) ? (((a.l / MO_S) & 1) ? (a.r > b.r) : (a.r < b.r)) : (a.l < b.l);}
}query[M];
/*====================*/
lnt moans[M];
/*====================*/
struct Query2
{int l, r, sign, idx;Query2(int _l = 0, int _r = 0, int _sign = 0, int _idx = 0){l = _l, r = _r, sign = _sign, idx = _idx;}
};
vector<Query2>sufquery[N];
vector<Query2>prequery[N];
/*====================*/
const int Block_S = 300;
const int Block_B = N / Block_S + 10;
/*====================*/
int belong[N];
struct Block
{int l, r;Block(void){l = r = 0;}
}block[Block_B];
/*====================*/
void Solve(void)
{do{cin >> n >> m;for (int i = 1; i <= n; ++i){cin >> a[i];}for (int i = 1; i <= m; ++i){int l, r; cin >> l >> r;query[i] = Query1(l, r, i);}} while (false);//读入do{vector<int>lib;for (int i = 1; i <= n; ++i){lib.push_back(a[i]);}sort(lib.begin(), lib.end());lib.erase(unique(lib.begin(), lib.end()), lib.end());for (int i = 1; i <= n; ++i){a[i] = lower_bound(lib.begin(), lib.end(), a[i]) - lib.begin() + 1;}} while (false);//离散化do{C_FenwickTree<int>tree; tree.Init(n);for (int i = 1; i <= n; ++i){preupper[i] = tree.Ask(a[i] + 1, n); tree.Add(a[i], 1);}} while (false);//预处理前缀upperdo{C_FenwickTree<int>tree; tree.Init(n);for (int i = n; i >= 1; --i){suflower[i] = tree.Ask(1, a[i] - 1); tree.Add(a[i], 1);}} while (false);//预处理后缀lowerdo{sort(query + 1, query + 1 + m);int l = 1, r = 0;for (int i = 1; i <= m; ++i){if (query[i].l < l)sufquery[r + 1].push_back(Query2(query[i].l, l - 1, -1, i));while (query[i].l < l)moans[i] += suflower[--l];if (r < query[i].r)prequery[l - 1].push_back(Query2(r + 1, query[i].r, -1, i));while (r < query[i].r)moans[i] += preupper[++r];if (l < query[i].l)sufquery[r + 1].push_back(Query2(l, query[i].l - 1, +1, i));while (l < query[i].l)moans[i] -= suflower[l++];if (query[i].r < r)prequery[l - 1].push_back(Query2(query[i].r + 1, r, +1, i));while (query[i].r < r)moans[i] -= preupper[r--];}} while (false);//一次离线莫队do{for (int i = 1; i <= n; ++i){belong[i] = i / Block_S + 1;if (block[belong[i]].l == 0){block[belong[i]].l = i;}block[belong[i]].r = i;}} while (false);//预处理分块do{//查询[1,idx]中有多少数大于a[l~r]vector<int>cnt1(n + 10, 0), cnt2(belong[n] + 10, 0);for (int i = 1; i <= n; ++i){//插入a[i]for (int j = a[i]; j >= block[belong[a[i]]].l; --j)cnt1[j]++;for (int j = belong[a[i]]; j >= belong[1]; --j)cnt2[j]++;for (int j = 0; j < prequery[i].size(); ++j){int l = prequery[i][j].l;int r = prequery[i][j].r;for (int k = l; k <= r; ++k){//查询大于a[k]的个数if (a[k] + 1 <= n){moans[prequery[i][j].idx] += prequery[i][j].sign * (cnt1[a[k] + 1] + cnt2[belong[a[k] + 1] + 1]);}}}}} while (false);//二次离线莫队处理prequerydo{//查询[idx,n]中有多少数小于a[l~r]vector<int>cnt1(n + 10, 0), cnt2(belong[n] + 10, 0);for (int i = n; i >= 1; --i){//插入a[i]for (int j = a[i]; j <= block[belong[a[i]]].r; ++j)cnt1[j]++;for (int j = belong[a[i]]; j <= belong[n]; ++j)cnt2[j]++;for (int j = 0; j < sufquery[i].size(); ++j){int l = sufquery[i][j].l;int r = sufquery[i][j].r;for (int k = l; k <= r; ++k){//查询小于a[k]的个数if (a[k] - 1 >= 1){moans[sufquery[i][j].idx] += sufquery[i][j].sign * (cnt1[a[k] - 1] + cnt2[belong[a[k] - 1] - 1]);}}}}} while (false);//二次离线莫队处理sufquerydo{for (int i = 1; i <= m; ++i){moans[i] += moans[i - 1];}vector<lnt>ans(m + 1, 0);for (int i = 1; i <= m; ++i){ans[query[i].idx] = moans[i];}for (int i = 1; i <= m; ++i){cout << ans[i] << endl;}} while (false);//处理输出答案
}
/*====================*/
int main()
{
#ifndef ONLINE_JUDGEfreopen("IN.txt", "r+", stdin);
#endifios::sync_with_stdio(false);cin.tie(NULL), cout.tie(NULL);int T = 1; //cin >> T;while (T--)Solve();return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

[网鼎杯 2020 朱雀组]Nmap

这题考察的是nmap写一句话木马的知识 可以输入形如 <?php eval($_POST[1]);?> -oG 1.php 来写入一句话 经过测试这题过滤了php,我们就是用短标签绕过 -oG b.phtml ` 这段命令的作用是:将扫描结果保存到b.phtml中,同时这个phtml文件还包含了前面的一句话木马 目的就是…

电力煤矿液体泄漏识别系统

电力煤矿液体泄漏识别系统对电力煤矿危化品生产区域管道机械实时检测,当电力煤矿液体泄漏识别系统检测到机械管道出现液体泄漏时,系统立即抓拍存档并告警同步回传给报警信息给后台监控人员,让工作人员及时处理,电力煤矿液体泄漏识别系统实现危险区域跑冒滴漏异常自动监控抓…

河道水尺水位监测系统

河道水尺水位监测系统利用计算机视觉技术对河道湖泊水尺水位进行7*24小时全天候实时监测,当河道水尺水位监测系统监测到河道水位异常变化时,系统立即抓拍存档同步回传图片给后台监控平台,提醒后台工作人员及时处理异常情况,避免更大损失的发生。河道水尺水位监测系统适用于…

7-4DeepFM模型

DeepFM继承了Wide&Deep的主体结构,将高低特征进行融合。其主要创新点有2个。一是将Wide部分替换成了 FM结构,以更有效的捕获特征交互interaction;二是FM中的隐向量 和 Deep部分的 embedding 向量共享权重,减少模型复杂性。推荐系统和广告CTR预估主流模型的演化有两条主…

排水口排水识别系统

排水口排水识别系统基于Python基于YOLOv7深度学习的计算机视觉识别检测算法,排水口排水识别系统赋予传统监测系统智能检测能力提升企业污水排放监督管效率,7*24小时不间断准确判断检测场景内的是否出现排水口排水情况,减少后台监控人员的工作量,减少后台漏报误报产生的失误…

作业区域人数超员预警系统

作业区域人数超员预警系统利用现场已有摄像头对生产作业区域进行全天候不间断实时监测,一旦作业区域人数超员预警系统监测到作业区域人数超员时,立即进行抓拍存档并告知后台监控中心人员,提醒后台人员及时处理避免意外情况发生。作业区域人数超员预警系统对人数超员记录截图…

河道采砂船监测识别系统

河道采砂船监测识别系统通过计算机视觉深度学习技术对河道采砂区域进行实时监测,当河道采砂船监测识别系统监测到有采砂船通过停留非法采砂时,立即抓拍存档触发告警,同步回传给后台通知后台人员及时处理。河道采砂船监测识别系统对河道采砂区域进行7*24小时实时监测有效弥补…

仪表盘读数识别系统

仪表读数识别系统利用现场传统监控摄像头对仪表盘刻度数进行7*24小时实时读取,当仪表盘读数识别系统监测到仪表盘数据异常时,立刻推送给后台相关管理人员,工作人员在第一时间到现场进行处理,避免更大的损失发生。同时,将告警截图和视频保存到数据库形成报表。仪表盘读数识…