题目链接:
题目大意:
给
N
N
N 个数,
a
1
,
a
2
,
…
,
a
N
a_1, a_2, \ldots,a_N
a1,a2,…,aN,
求所有区间的逆序对数之和。
外话:我是真的太久没有写 Blog 了,
数据范围:
对于 30% 的数据, 1 ≤ N ≤ 1000 1 \leq N \leq 1000 1≤N≤1000
对于 60% 的数据, 1 ≤ N ≤ 10000 1 \leq N \leq 10000 1≤N≤10000
对于 100% 的数据, 1 ≤ N ≤ 100000 1 \leq N \leq 100000 1≤N≤100000
解题思路:
- 先考虑暴力的做法:
两层循环 i , j i, j i,j 暴力扫,假设 i < j i \lt j i<j,
当 a [ i ] > a [ j ] a[i] \gt a[j] a[i]>a[j] 时,就可以
\quad 在区间 [ 1 , i ] [1, i] [1,i] 中任意挑一个位置作为 左端点;
\quad 在群建 [ j , N ] [j, N] [j,N] 中任意挑一个位置作为 右端点;
这样对最后答案 a n s ans ans 的贡献就是 i ∗ ( N − j + 1 ) i * (N-j+1) i∗(N−j+1) ,画出图来就是这样:
就是蓝色两段长度的乘积。 - 再考虑优化:
对于一个位置 i i i,如果知道 它往后 所有比它小的数 的后缀长度 的和,那就知道了 i i i 这个位置对答案的贡献;
说的更数学化就是,
设 ( p 1 , p 2 , ⋯   , p k ) (p_1, p_2, \cdots,p_k) (p1,p2,⋯,pk) 为在 i i i 位置之后 且比 a [ i ] a[i] a[i] 小的 所有位置;
即 i < p j i \lt p_j i<pj 且 a [ i ] > a [ p j ]    ( 1 ≤ j ≤ k ) a[i] \gt a[p_j] \,\, (1 \leq j \leq k) a[i]>a[pj](1≤j≤k)
那么, i i i 位置对答案 a n s ans ans 的贡献为 i × ∑ j = 1 k ( n − p j + 1 ) i \times \sum \limits_{j=1}^k (n - p_j + 1) i×j=1∑k(n−pj+1) ;
画出图来就是:
最后就是要求前缀和,就一个树状数组就可以了。
AC 代码:
/**********************************************
*Author* :XzzF
*Created Time* : 2019/1/27 15:56:59
*Ended Time* : 2019/1/27 16:01:37
*********************************************/
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <stack>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 100000;
int n;
int a[MaxN + 5];
LL C[MaxN + 5];
inline int lowbit(int x) {
return x & (-x);
}
LL suffix(int end) {
LL res = 0LL;
while(end > 0) {
res += C[end];
end -= lowbit(end);
}
return res;
}
void modify(int pos, int val) {
while(pos <= n) {
C[pos] += val;
pos += lowbit(pos);
}
}
int main()
{
while(scanf("%d", &n) != EOF)
{
memset(C, 0, sizeof(C));
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
LL ans = 0LL;
for(int i = n; i >= 1; i--) {
ans += 1LL * suffix(a[i]) * i; //统计前缀
modify(a[i], n - i + 1); //在对应位置加上 当前的后缀长度
}
printf("%lld\n", ans);
}
return 0;
}