题解 P1908 【逆序对】
好像大部分人都是利用归并排序做的
那么我讲一下树状数组的逆序对吧
正常的暴力思路我就不讲了
另类的暴力思路(20分):桶排思想,每拿到一个值放入桶中,统计所有大于这个数的数量,然后累加
优化:离散化,因为n<=40000,所以只需要存那个数在数组中是第几大就好了。(然而并没有什么用)离散代码片段:
bool cmp1(const int &x,const int &y)
{
return b[x]>b[y]; //对编号进行排序
}
int main()
{
int ans=0;
scanf("%d",&n);
for(register int i=1;i<=n;i++)
{
scanf("%d",&b[i]); //读入
a[i]=i;//顺便初始化一下
}
sort(a+1,a+n+1,cmp1); //排序
//这里略过接下来的部分,a[]就是我们要用的"第几大"数组
}
正解(100分 56ms 2MB):树状数组
由暴力思路2,我们发现需要不断的求和和修改,所以引出树状数组,建议大家在手上模拟一遍
如样例:
设b[]为读入的数组,a[]为要离散化后的数组,C[]为树状数组,A[]为树状数组对应对数组(被求和的数组),sum(x)为求
6
5 4 2 6 3 1
1)进行离散化操作,处理数据(这里储存的是在数组中是第几大的):a[]={2,3,5,1,4,6}
2)然后加入
3)加入
4)加入
5)加入
6)加入
6)加入
8)输出ans
代码:
#include<algorithm>
#include<cstdio>
using namespace std;
int n,m,c[40005]={0},a[40005]={0},b[40005]={0}; //定义数组,b[]为读入的数组,a[]为要离散化后的数组,C[]为树状数组
inline void Add(register int x) //树状数组加入
{
for(;x<=n;x+=(x&-x))
c[x]++; //因为这里只需要1,所以我就写出c[x]++了
}
inline int Sum(register int x) //树状数组求和,同上面的sum(x)
{
register int s=0; //计数的变量
for(;x>0;x-=(x&-x)) //累计
s+=c[x];
return s; //返回结果
}
bool cmp1(const int &x,const int &y) //离散化需要用的,上面有讲
{
return b[x]>b[y];
}
int main()
{
int ans=0; //ans为最后的结果
scanf("%d",&n); //读入n
for(register int i=1;i<=n;i++)
{
scanf("%d",&b[i]); //读入
a[i]=i; //顺便初始化一下a数组
}
sort(a+1,a+n+1,cmp1); //给a数组排序,达到最终的效果
for(register int i=1;i<=n;i++)
{
Add(a[i]); //依次加入树状数组
ans+=Sum(a[i]-1); //计算结果并累计
}
printf("%d",ans); //输出ans
return 0; //返回
}