今天给大家带来的是求逆序对个数。
我会归并!
当然,还是用权值线段树解决
都是板子,一摸一样,双倍积分
首先,逆序对是什么?
对于给定的一段正整数序列,逆序对就是序列中ai>aj且i< j的有序对
通俗来讲,就是有一列数,如果有这样两个数m,n满足
- m在n前面
- m比n大
那么m,n就是一对逆序对,现在我们要求的就是这种逆序对个数。
我们能够想到这样一种方法:
- 读入一个数,查找之前读入的数中比它大的数的个数,累加答案
- 把它丢进某种数据结构中
而这“某种数据结构”不就是权值线段树吗?
查询:
对于查询数x与此时左子树右边界mid( mid=(l+r)>>1 )
若x<=mid,则答案=左子数比x大的数的个数+右子树的数的个数
若x>mid,则答案=右子树比x大的数的个数
递归处理即可
一定记住权值线段树下标维护的是值域!!
插入:
不是与原来一摸一样吗
BUT
a[i]<=INT_MAX
权值线段树会爆!!!
怎么办?
n好像十分友善:n\(<=\)\(5*10^{5}\)
离散化!
我们发现并不需要记录每一个数的具体数值,只要记录他们之间的大小关系即可。
- 把所有的数读进来
- 排序
- 按顺序从1开始标号作为现在的数列
晦涩啊!举个例子:
初始读入:5 123 777 12 4 12
对应下标:1 2 3 4 5 6
排序后:4 5 12 12 123 777
对应下标:5 1 4 6 2 3
从左向右枚举:
4------------------a[5]=1;
5!=4--------------a[1]=2;
12!=5------------a[4]=3;
12==12----------a[6]=a[4]=3;
123!=12---------a[2]=4;
777!=123--------a[3]=5;
所以离散化后的数列:2 4 5 3 1 3
节省了空间又能反应大小关系
接下来就是代码了:
#include<bits/stdc++.h>
using namespace std;
long long a[500005],ans,n;
struct MM{
long long ii,num;
}b[500005];
struct MMM{
long long l,r,sum;
}tree[2000005];
bool cmp(MM x,MM y)
{
return x.num<y.num;
}
void build(long long root,long long L,long long R)
{
tree[root].l=L;
tree[root].r=R;
if(tree[root].l==tree[root].r)
return;
long long mid=(L+R)>>1;
build(root<<1,L,mid);
build(root<<1|1,mid+1,R);
return;
}
void updata(long long root,long long v)
{
if(tree[root].l==tree[root].r)
{
tree[root].sum++;
return;
}
long long mid=(tree[root].l+tree[root].r)>>1;
if(v<=mid) updata(root<<1,v);
else updata(root<<1|1,v);
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
return;
}
long long query(long long root,long long x)
{
if(tree[root].l==tree[root].r) return tree[root].sum;
long long mid=(tree[root].l+tree[root].r)>>1;
if(x<=mid) return query(root<<1,x)+tree[root<<1|1].sum;
return query(root<<1|1,x);
}
int main()
{
cin>>n;
for(long long i=1;i<=n;i++)//这里离散化开始
{
cin>>a[i];
b[i].num=a[i];
b[i].ii=i;
}
sort(b+1,b+n+1,cmp);
for(long long i=1,j=0;i<=n;i++)
{
if(b[i].num!=b[i-1].num||i==1) j++;
a[b[i].ii]=j;
}//这里离散化结束,可以看看或者自己算算加深理解
build(1,1,n);
for(long long i=1;i<=n;i++)
{
ans+=query(1,a[i]+1);//这里有个小细节:为什么要加一呢?因为权值线段树算的是大于等于x的数的个数,而我们要求的是严格大于x的数的个数,所以加一(具体可以看看查询函数或手算试试)
updata(1,a[i]);
}
cout<<ans;
return 0;
}
原文地址:https://www.cnblogs.com/zmyzmy/p/9595230.html
时间: 2024-10-12 18:12:45