树状数组求解全局逆序对问题

基础

对于一维数组求解严格逆序对问题,需要用到一个技巧,就是把数字作为树状数组的下标,序列{5,4,2,6,3,1},对应a[5] a[4] a[2] a[6] a[3] a[1]。每处理一个数字,树状数组的下标对应的元素数值+1,统计前缀和,就是逆序对的数量。
可以逆序或者正序做,推荐逆序。

  • 比如说逆序求解:
    对于上面提到的序列{5,4,2,6,3,1}
    数字1,把a[1]+1,计算a[1]的前缀和sum(0),逆序对数量ans=ans+sum(0)=0;
    数字3,把a[3]+1,计算a[3]的前缀和sum(2),逆序对数量ans=ans+sum(2)=0;
    数字6,把a[6]+1,计算a[6]的前缀和sum(5),逆序对数量ans=ans+sum(5)=0;
    ...
  • 再看看正序求解,当前已经处理的数字个数减掉当前数字的前缀和即为以该数为较小数的逆序对的个数
    数字5,把a[5]+1,当前处理了1个数,ans=ans+(1-sum(5))=0;
    数字4,把a[4]+1,当前处理了2个数,ans=ans+(1-sum(4))=1;
    数字2,把a[2]+1,当前处理了3个数,ans=ans+(1-sum(2))=3;
    ...

又一个例子:
image

例题

  1. cf A. Inversions
    求解数组数当前数前逆序对的个数
//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
//#define int long long

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}
	return x*f;
}

using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
https://codeforces.com/edu/course/2/lesson/4/3/practice/contest/274545/problem/A
*/
const int maxm=1e5+5,inf=0x3f3f3f3f,mod=998244353;
ll n,a[maxm],b[maxm],ans[maxm];

int lowbit(int x){return x&(-x);}

void update(int x,int v){
	while(x<=n){
		b[x]+=v;
		x+=lowbit(x);
	}
	return ;
}

ll getsum(int x){
	ll ans=0;
	while(x){
		ans+=b[x];
		x-=lowbit(x);
	}
	return ans;
}

void solve(){
	int c=0;
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=n;++i){//正序求解
		update(a[i],1);
		ans[i]=c-getsum(a[i]-1);//当前已经处理的数字个数-比当前数字小的数字个数即为在当前位置前大于当前数字的数字个数
		++c;
	}
	for(int i=1;i<=n;++i){
		cout<<ans[i]<<" \n"[i==n];
	}
	return ;
}

signed main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int _=1;
//	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

相关资料

  1. oi wiki - 全局逆序对
posted on 2023-06-28 09:45  Qiansui  阅读(8)  评论(0编辑  收藏  举报