NC14522 珂朵莉的数列
题目
题目描述
珂朵莉给了你一个序列,有 \(\frac{n\times(n+1)}2\) 个子区间,求出她们各自的逆序对个数,然后加起来输出
输入描述
第一行一个数 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
题解
知识点:线段树,离散化,枚举。
这道题在经典的逆序对问题上加了一点点东西。
对于经典逆序对问题,是通过从左到右枚举每个数,对于某个数求出在它左边且大于它的数的个数,利用线段树或树状数组的权值和解决的。在这道题,我们可以借用这个思路。
假设数组 \(a\) 有 \(n\) 个数,考虑一对逆序对 \((x,y),x<y\) 产生的贡献,显然是包含其的区间个数 \(x(n-y+1)\) ,其中 \(a_x\) 贡献了 \(x\) 个有效点, \(a_y\) 贡献了 \(n - y + 1\) 个有效点。
我们固定右端点 \(y\) ,那么产生的贡献为 \((n-y+1)\displaystyle \sum_{x<y,a_x > a_y}x\) ,只需要求出在它左边且大于它的数贡献的有效点的个数和,只需要将逆序对问题中的权值从个数替换为贡献的有效点个数即可。
因此,我们从左到右枚举每个数(枚举顺序维护了在左边的偏序关系),用权值线段树维护出现过的数的权值(有效点个数),随后只需要询问大于当前数的权值和(线段树维护了大于的偏序关系),即为 \(\displaystyle \sum_{x<y , a_x>a_y}x\) 。
另外,本题数据范围要离散化,结果超过 long long
,要用 __int128_t
。
时间复杂度 \(O(n \log n)\)
空间复杂度 \(O(n)\)
代码
#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