luogu P6186 [NOI Online 提高组]冒泡排序 |树状数组

题目描述

给定一个 1 ∼ n的排列 p_i​,接下来有 m 次操作,操作共两种:

交换操作:给定 x,将当前排列中的第 x 个数与第 x+1 个数交换位置。
询问操作:给定 k,请你求出当前排列经过 k 轮冒泡排序后的逆序对个数。 对一个长度为 n 的排列 p_i​ 进行一轮冒泡排序的伪代码如下:

for i = 1 to n-1:
if p[i] > p[i + 1]:
swap(p[i], p[i + 1])

输入格式

第一行两个整数 n,m,表示排列长度与操作个数。

第二行 n 个整数表示排列 p_i​。

接下来 m 行每行两个整数 t_i​,c_i​,描述一次操作:

若 t_i=1,则本次操作是交换操作,x=c_i​;
若 t_i=2,则本次操作是询问操作,k=c_i。

输出格式

对于每次询问操作输出一行一个整数表示答案。


找规律

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=4e5+10;
#define int long long
int before[N],record[N],a[N],n,m;
int tree[N];
inline void add(int x,int y){
	for(;x<=n;x+=x&(-x))tree[x]+=y;	
}
inline int sum(int x){
	int res=0;
	for(;x;x-=x&(-x))res+=tree[x];
	return res;	
}
signed main(){
	cin>>n>>m;
	int tot=0;
	for(int i=0;i<n;i++){
		scanf("%lld",&a[i]);
		before[i]=i-sum(a[i]);
		tot+=before[i];
		record[before[i]]++;
		add(a[i],1);
	}
	memset(tree,0,sizeof(tree));
	add(1,tot);
	tot=0;
	for(int i=0;i<n;i++){
		tot+=record[i];
		add(i+2,tot-n);
	}
	while(m--){
		int opt,x;
		scanf("%lld%lld",&opt,&x);
		x=min(x,n-1);
		if(opt==1){
			x--;
			if(a[x]<a[x+1]){
				swap(a[x],a[x+1]);
				swap(before[x],before[x+1]);
				add(1,1);
				add(before[x+1]+2,-1);
				before[x+1]++;
			}else{
				swap(a[x],a[x+1]);
				swap(before[x],before[x+1]);
				add(1,-1);
				before[x]--;
				add(before[x]+2,1);
			}
		}else printf("%lld
",sum(x+1));
	}
}
原文地址:https://www.cnblogs.com/naruto-mzx/p/12666816.html