一直想不明白要怎么建树,由于是满足 i < j and ai > aj的条件,所以其实就是对于每个数aj,求比它大的数的个数。建树的时候把每个节点都初始化为0,每当插入一个数,就在这个数对应的叶子节点上加1,同时更新包含这个点的线段所对应的非叶子节点(加1),一层层更新上去,区间求和。这样如果存在某个数,在相应查找的时候就会找到。由于是要找比aj大的数,所以查找范围就是aj-(n-1).
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=5005;
int num[maxn<<2];
void build(int l,int r,int rt)
{
num[rt]=0;
if(l==r)
return ;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
void update(int k,int l,int r,int rt)
{
if(l==r)
{
num[rt]++;
return ;
}
int m=(l+r)>>1;
if(k<=m)
update(k,l,m,rt<<1);
else
update(k,m+1,r,rt<<|1);
num[rt]=num[rt<<1]+num[rt<<1|1];
}
int query(int ll,int rr,int l,int r,int rt)
{
if(ll<=l&&rr>=r)
return num[rt];
int ans=0;
int m=(l+r)>>1;
if(l<=m)
ans+=query(ll,rr,l,m,rt<<1);
if(r>m)
ans+=query(ll,rr,m+1,r,rt<<1|1);
return ans;
}
int a[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=-1)
{
int sum=0;
build(1,n,1);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=query(a[i]+1,n,1,n,1);
update(a[i]+1,1,n,1);
}
int ans=sum;
for(int i=0;i<n;i++)
{
sum+=(n-a[i]*2-1);
//公式推导,把最前面的数a[i]放到最后相当于原逆序数n个数少了a[i],多了n-a[i]+1个;
ans=min(ans,sum);
}
printf("%d\n",ans);
}
return 0;
}
由于数列是0到n的 有规律 可以直接模拟
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int a[5005];
int main()
{
int n;
while(cin>>n)
{
int sum=0;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(a[i]>a[j])sum++;
int minn=sum;
for(int i=1;i<=n;i++)
{
sum=sum-2*a[i]+n-1;
if(minn>sum)
minn=sum;
}
cout<<minn<<endl;
}
return 0;
}