P1908 逆序对-线段树题解

	读完题目,你的第一想法肯定是直接暴力就能过了。实际上在看完数据之后。。。QAQ。好吧,肯定不能暴力,那
么现在就要去寻找它的特征。根据它的特征,我们可以分析出要用线段树去做这道题。那么当我们确定这个算法时,我
们就要找下一个难点:如何建立这个树?这里就直接告诉你叭:首先,我们要根据大小将初始数组排列,然后再根据原
来的顺序再排回来,然后根据每一个数的大小对号入座,在入座的过程中我们计算在这个叶子节点的右边有多少个已经
入座的元素,这时候就体现出来了线段树的便捷:查询效率为O(log n) 级别。然后使用sum分别记录,最后输出sum。
/*
ID: erictian2018
TASK:
LANG: C++
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define inf 0x7ffffff
#define ll long long
//#include 
//#include 
using namespace std;
/*
输入:
6
5 4 2 5 3 1
输出:

*/
ll scan() {
     
	ll x=0;
	ll flag=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') {
     
		ch=getchar();
		if(ch=='-')
			flag=1;
	}
	for(; ch>='0'&&ch<='9'; ch=getchar()) x=x*10+ch-'0';
	if(!flag)
		return x;
	return -x;
}
ll n;
struct node{
     
	ll l,r;
	ll cnt;cnt是指l到r有多少个元素 
}tree[2000010];
ll sum;/求有多少个逆序对 
struct st{
     
	ll num,rank,order;//rk: 排序后的编号 
}a[500100];
// 
bool cmp1(st a,st b){
     
	if(a.num==b.num)
	return a.order<b.order;
	return a.num<b.num;
}
// 
bool cmp2(st a,st b){
     
	return a.order<b.order;
}
// 
void build(ll l,ll r,ll k){
     
	tree[k].l=l;
	tree[k].r=r;
	if(l==r)
		return ;
	ll mid=(l+r)/2;
	build(l,mid,k*2);
	build(mid+1,r,k*2+1);
}
// 
ll ask(ll l,ll r,ll k){
     
	if(l<=tree[k].l&&tree[k].r<=r)
		return tree[k].cnt;
	ll mid=(tree[k].l+tree[k].r)/2;
	ll value=0;
	if(l<=mid)
	value+=ask(l,r,k*2);
	if(r>mid)
	value+=ask(l,r,k*2+1);
	return value;
}
// 
void change(ll l,ll r,ll k){
     
	if(l==tree[k].l&&tree[k].r==r){
     
		tree[k].cnt++;
		sum+=ask(l+1,n,1);
		return ;
	}
	ll mid=(tree[k].l+tree[k].r)/2;
	if(l<=mid)
	change(l,r,k*2);
	if(r>mid)
	change(l,r,k*2+1);
	tree[k].cnt=tree[k*2].cnt+tree[k*2+1].cnt;
}
int main() {
     
//ofstream fout ("test.out");
//ifstream fin ("test.in");
n=scan();
for(ll i=1;i<=n;i++){
     
	a[i].num=scan();
	a[i].order=i;
}
//从小到大排序   如果有相同的数则下标较小的排在前面 
sort(a+1,a+n+1,cmp1);
for(ll i=1;i<=n;i++)
	a[i].rank=i;
//根据order的下标再排回来 
sort(a+1,a+n+1,cmp2);
build(1,n,1);
for(ll i=1;i<=n;i++)
change(a[i].rank,a[i].rank,1);
cout << sum << endl;
	return 0;
}

你可能感兴趣的:(P1908 逆序对-线段树题解)