hihoCoder-1926-逆序对计数

这篇博客讨论了hihoCoder上的一个编程问题,涉及计算所有区间的逆序对数量。博主首先介绍了暴力求解的方法,即通过两层循环计算逆序对,然后提出了优化策略,利用树状数组计算前缀和以提高效率。提供了100%数据范围内的解题思路,并附有AC代码。
摘要由CSDN通过智能技术生成
题目链接:

[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 &lt; j i \lt j i<j
    a [ i ] &gt; 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) ,画出图来就是这样:
    暴力
    就是蓝色两段长度的乘积。
  2. 再考虑优化:
    对于一个位置 i i i,如果知道 它往后 所有比它小的数 的后缀长度 的和,那就知道了 i i i 这个位置对答案的贡献;
    说的更数学化就是,
    ( p 1 , p 2 , ⋯ &ThinSpace; , p k ) (p_1, p_2, \cdots,p_k) (p1,p2,,pk) 为在 i i i 位置之后 且比 a [ i ] a[i] a[i] 小的 所有位置;
    i &lt; p j i \lt p_j i<pj a [ i ] &gt; a [ p j ] &ThinSpace;&ThinSpace; ( 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)


    画出图来就是:
    优化
    最后就是要求前缀和,就一个树状数组就可以了。


    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值