题解 P5463 小鱼比可爱(加强版)
题意简述
给定一个长度为 \(n\) 的序列 \(a\) ,令 \(g(l,r)\) 表示 区间 \([l,r]\) 的逆序对数,求:
\[\sum_{l=1}^n\sum_{r=l+1}^n g(l,r)
\]
\(1 \leq n \leq 10^6 ,1 \leq a_i \leq 10^9\) 。
Solution
考虑一对逆序对 \((i,j)\) \((i<j)\) 在哪些区间出现过,显然,左端点 \(\leq i\) ,右端点 \(\geq j\) 的区间都包含这一对逆序对,所以这对逆序对一共在 \(i\times (n-j+1)\) 个区间内出现过。
答案就是每队逆序对出现的次数和,写成式子就是:
\[\sum_{i=1}^n\sum_{j=1}^{i-1}j\times(n-i+1)[a_j>a_i]
\]
把与 \(j\) 无关的提前:
\[\sum_{i=1}^n(n-i+1)\sum_{j=1}^{i-1}j[a_j>a_i]
\]
枚举 \(i\) ,后面这一部分就变成了一个二维偏序,可以使用树状数组解决。
注意到 \(1\leq a_i \leq 10^9\) ,所以需要离散化。
答案会爆 long long
,要不写高精,要不就用__int128_t
,这里采用的是后者。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
typedef __int128_t LL;
char buf[1 << 25] ,*p1 = buf ,*p2 = buf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf ,1 ,1 << 21 ,stdin) ,p1 == p2) ? EOF : *p1++)
using namespace std;
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
inline void write(LL x) {
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
inline void writeln(LL x) {write(x); puts("");}
const int N = 1e6 + 5;
struct BIT {
LL t[N]; int n;
inline void modify(int x ,int k) {
for (int i = x; i <= n; i += i & (-i)) t[i] += k;
}
inline LL query(int x) {
LL ans = 0;
for (int i = x; i ; i -= i & (-i)) ans += t[i];
return ans;
}
}t;
int n ,a[N] ,nums[N] ,idx;
signed main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = nums[i] = read();
sort(nums + 1 ,nums + n + 1);
idx = unique(nums + 1 ,nums + n + 1) - nums - 1;
long long sum = 0; LL ans = 0;
t.n = idx;
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(nums + 1 ,nums + idx + 1 ,a[i]) - nums;
ans += (n - i + 1) * (sum - t.query(a[i]));
sum += i;
t.modify(a[i] ,i);
}
writeln(ans);
return 0;
}