NC14522 珂朵莉的数列
题目
题目描述
珂朵莉给了你一个序列,有 个子区间,求出她们各自的逆序对个数,然后加起来输出
输入描述
第一行一个数 n 表示这个序列 a 的长度之后一行 n 个数,第i个数表示ai
输出描述
输出一行一个数表示答案
示例1
输入
10 1 10 8 5 6 2 3 9 4 7
输出
270
示例2
输入
20 6 0 4 5 8 8 0 6 6 1 0 4 6 6 0 0 7 2 0 5
输出
3481
备注
对于100%的数据,n <=1000000 ,0 <= 序列中每个数 <= 1000000000
题解
知识点:线段树,离散化,枚举。
这道题在经典的逆序对问题上加了一点点东西。
对于经典逆序对问题,是通过从左到右枚举每个数,对于某个数求出在它左边且大于它的数的个数,利用线段树或树状数组的权值和解决的。在这道题,我们可以借用这个思路。
假设数组 有 个数,考虑一对逆序对 产生的贡献,显然是包含其的区间个数 ,其中 贡献了 个有效点, 贡献了 个有效点。
我们固定右端点 ,那么产生的贡献为 ,只需要求出在它左边且大于它的数贡献的有效点的个数和,只需要将逆序对问题中的权值从个数替换为贡献的有效点个数即可。
因此,我们从左到右枚举每个数(枚举顺序维护了在左边的偏序关系),用权值线段树维护出现过的数的权值(有效点个数),随后只需要询问大于当前数的权值和(线段树维护了大于的偏序关系),即为 。
另外,本题数据范围要离散化,结果超过 long long
,要用 __int128_t
。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; template<class T> struct Discretization { vector<T> uniq; Discretization() {} Discretization(const vector<T> &src) { init(src); } void init(const vector<T> &src) { uniq = src; sort(uniq.begin() + 1, uniq.end()); uniq.erase(unique(uniq.begin() + 1, uniq.end()), uniq.end()); } int get(T x) { return lower_bound(uniq.begin() + 1, uniq.end(), x) - uniq.begin(); } }; struct T { ll sum; static T e() { return { 0 }; } friend T operator+(const T &a, const T &b) { return { a.sum + b.sum }; } }; struct F { int add; T operator()(const T &x) { return { x.sum + add }; } }; template<class T, class F> class SegmentTree { int n; vector<T> node; void update(int rt, int l, int r, int x, F f) { if (r < x || x < l) return; if (l == r) return node[rt] = f(node[rt]), void(); int mid = l + r >> 1; update(rt << 1, l, mid, x, f); update(rt << 1 | 1, mid + 1, r, x, f); node[rt] = node[rt << 1] + node[rt << 1 | 1]; } T query(int rt, int l, int r, int x, int y) { if (r < x || y < l) return T::e(); if (x <= l && r <= y) return node[rt]; int mid = l + r >> 1; return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y); } public: SegmentTree(int _n = 0) { init(_n); } void init(int _n) { n = _n; node.assign(n << 2, T::e()); } void update(int x, F f) { update(1, 1, n, x, f); } T query(int x, int y) { return query(1, 1, n, x, y); } }; template<class T> inline void write(T x) { if (x < 0) { putchar('-');x = -x; } if (x >= 10) write(x / 10); putchar(x % 10 + '0'); } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin >> n; vector<int> a(n + 1); for (int i = 1;i <= n;i++) cin >> a[i]; Discretization<int> dc(a); int n_rk = dc.uniq.size() - 1; SegmentTree<T, F> sgt(n_rk); // 假设i < j,若有逆序对(i,j),那么贡献为i*(n-j+1) // 从左往右枚举逆序对右侧的数字a[i],则可以累加左侧数字,即[1,i-1],>a[i]的数的贡献,最后乘n-i+1即可 __int128_t ans = 0; for (int i = 1;i <= n;i++) { ans += sgt.query(dc.get(a[i]) + 1, n_rk).sum * (n - i + 1); sgt.update(dc.get(a[i]), { i }); } write(ans); puts(""); return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17366713.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧