树状数组+逆序对数的树状数组实现+楼兰图腾tyvj1432

逆序对数使用归并排序更好,在此我们介绍一种树状数组的实现。

假设一个数组A[n],当A[n]=0时表示数字n在序列中没有出现过,A[n]=1表示数字n在序列中出现过。A对应的树状数组为c[n],则c[n]维护的是数组A[n]前缀和,即树状数组c可用于求A中某个区间的值的和

         所以要求序列中比元素a大的数的个数,可以用i - ask(a)即可( i 表示此时序列中元素的个数)。

 

序列的变化(下划线为新增加元素)

序列中大于新增加的数字的个数

操作

{ }

0

初始化时序列中一个数都没有

{4 }

0

往序列中增加4,统计此时序列中大于4的元素个数

{4 3 }

1

往序列中增加3,统计此时序列中大于3的元素个数

{4 3 2}

2

往序列中增加2,统计此时序列中大于2的元素个数

{4 3 2 1}

3

往序列中增加1,统计此时序列中大于1的元素个数

楼兰图腾tyvj1432 ,用left\right数组 储存每个数的左右正逆序个数,可以省时间 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include <ctime>
#include "queue"
using namespace std;
#define SIZE 200005
#define SIZES 200020
int a[SIZE];//a为目标序列
int tree[SIZES]; //SIZES记录了a最大值大
unsigned long long ans1,ans2;
int n; 
int maxn;
int left1[SIZES],right1[SIZES];
int left2[SIZES],right2[SIZES];

int ask(int x)
{
	int ans = 0;
	for (; x ; x -= x&-x )
		ans += tree[x];
	return ans;
}

void add(int x, int a)
{
	for ( ; x <= maxn ; x += x&-x )
		tree[x] += a;
}

int main(int argc, char const *argv[])
{

	scanf("%d",&n);
	for (int i = 1; i <=n; ++i)
	{
		scanf("%d",&a[i]);
		maxn=max(maxn,a[i]);
	}

	int i;

	memset(tree,0,sizeof(tree));
	for ( i = n; i >= 1  ; --i)
	{
		right1[i] = ask(a[i]);
		add(a[i],1);
	}	
	memset(tree,0,sizeof(tree));
	for ( i = 1; i <= n  ; ++i)
		{
			left1[i] = ask(a[i]);
			add(a[i],1);
		}
	
	memset(tree,0,sizeof(tree));
	for ( i = n; i >=1  ; --i)
		{
			right2[i] = ask(maxn) - ask(a[i]);
			add(a[i],1);
		}
	memset(tree,0,sizeof(tree));
	for ( i = 1; i <= n  ; ++i)
		{
			left2[i] = ask(maxn) - ask(a[i]);		
			add(a[i],1);
		}

	for (i= 2; i <n ; ++i)
	{
		ans1 += left1[i]*right1[i]; //^的个数
		ans2 += left2[i]*right2[i]; //V的个数
	}
	printf("%llu %llu\n",ans2,ans1);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值