T2 [NOI Online 提高组]冒泡排序 题解

T2 [NOI Online 提高组]冒泡排序

这道题目实在是牛逼,主要是这个思想需要转变

在这里插入图片描述


暴力模拟肯定不行

我们仔细观察一下:
	我们需要在第一时间内知道当前这个数字前面有多少个数字比这个数字大
	说人话:知道在0~i-1下标有多少个数字比a[i]大
	这个可以考虑统计一下树状数组统计一下比他小的数字有多少个(记作tot),前面有i个数字,反向求出比他大的数字。
第二:
	我们这么想:如果需要排序,我们一定要找到规律
	举个粟子:假设这个序列1 5 4 3 2
	我们可以看见2前面有5 4 3,3前面有5 4。
	排序一次后:1 4 3 2 5,排序一次我们发现2前面比2大的数字少了1 只有4 3,同理3只有1次了
	我们就计算一下:前面具有比这个数字大的数字有几个,借助桶排序统计一下
	比如:下标为0的数字有n个,代表着n个数字前面比它大的数字有0个。
	下标为1的数字有m个,代表着m个数字前面比它大的数字有1个
	我们排完序列,每次排一次序列,只要是没有归位的数字都要减去1,就是说前面具有before[i]个大于这个数字的数字,每次排一次序列,before[i] = max(before[i]-1,0),i从0~n
第三:
	这个涉及到区间更新,我们考虑差分计算,所以考虑树状数组和差分配合使用,减少时间复杂度

#include<iostream>
#include<algorithm>
using namespace std;
int input[200005], before[200005], record[200005];
int n;
long long tree[200005];
inline int lowbit(int x)
{
	return x & (-x);
}
void update(int x, long long val)
{
	for (; x <= n; x += lowbit(x))
		tree[x] += val;
	return;
}
long long query(int x)
{
	long long res = 0;
	for (; x > 0; x -= lowbit(x))
		res += tree[x];
	return res;
}

int main()
{
	int m;
	cin >> n >> m;
	long long sum = 0; 
	for (int i = 0; i < n; i++)
	{
		cin >> input[i];
		before[i] = i - query(input[i]);
		//i代表前面有多少个数字,query(input[i])寻找到前面比它小的数字出现了几次
		sum += before[i];
		//计算出来总共的逆序对
		record[before[i]]++;
		//桶排序,下标代表前面有i个逆序对,有record[i]个
		update(input[i], 1);//把新来的数字加入进去
	}
	memset(tree, 0, sizeof(tree)); 
	update(1, sum);//让1为总数			   
	sum = 0;
	for (int i = 0; i < n; ++i)
	{
		sum += record[i];
		//sum代表着多少个数字,归位了
		update(i + 2, -(n - sum)); //我们得到的数字放入i+2的地方
		//n-sum意味着有多少逆序对归位了,可以这样子理解就是:
		//比如record[0] = 4,record[1] = 1,record[2]=1,n = 6
		//-(n - sum)代表下标为1,2都要减去1,当sum = 5代表着i = 1,排序一次
		//一个逆序对,大的数字已经到了后面去了,而下标为2代表这前面有两个大数字
		//一次排序后还有一个,用过这个-(n - sum)代表着少了几个逆序对
	}
	for (int i = 0, opt, x; i < m; i++)
	{
		scanf("%d%d", &opt, &x);
		x = min(x, n - 1); 
		if (opt == 1)
		{
			x--;
			if (input[x] < input[x + 1])
			{
				swap(input[x], input[x + 1]);
				swap(before[x], before[x + 1]);
				update(1, 1);//逆序对+1					
				update(before[x + 1] + 2, -1);//差分法需要对后面继续修改
				//保证前面大小和一样,这个是差分性质 
				before[x + 1]++;			
			}
			else
			{
				swap(input[x], input[x + 1]);
				swap(before[x], before[x + 1]);
				update(1, -1);			   
				before[x]--;		   
				update(before[x] + 2, 1); 
			}
		}
		else
			printf("%lld\n", query(x + 1)); 
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值