hihoCoder-1926-逆序对计数

题目链接:

[Offer收割]编程练习赛92

题目大意:

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 1N1000

对于 60% 的数据, 1 ≤ N ≤ 10000 1 \leq N \leq 10000 1N10000

对于 100% 的数据, 1 ≤ N ≤ 100000 1 \leq N \leq 100000 1N100000

解题思路:

  1. 先考虑暴力的做法:
    两层循环 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(Nj+1) ,画出图来就是这样:
    hihoCoder-1926-逆序对计数_第1张图片
    就是蓝色两段长度的乘积。
  2. 再考虑优化:
    对于一个位置 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](1jk)
    那么, 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=1k(npj+1)


    画出图来就是:
    hihoCoder-1926-逆序对计数_第2张图片
    最后就是要求前缀和,就一个树状数组就可以了。


    AC 代码:
/**********************************************
 *Author*        :XzzF
 *Created Time*  : 2019/1/27 15:56:59
 *Ended  Time*  : 2019/1/27 16:01:37
*********************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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;
}

你可能感兴趣的:(others)