*题目大意:求逆序对的数目
题目数据:
n < 500,000,n表示最多有n组数据
0 ≤ a[i] ≤ 999,999,999,a[i]表示每个数最大是99999999
限制:
Time limit:7000 ms
Memory limit:65536 kB
*题目分析:
*n的数据在可建树范围内,而a[i]的数据超出可建树范围
*所以数据需要离散化处理。
题目数据范围:
记录逆序对对数的变量需要由long long 类型的数据保存,例如5 4 3 2 1 ,逆序对数:4+3+2+1=10;是所给数据的2倍了。
离散化不再解释,那么我们如何去维护这样一棵树呢?
**例如 4 8 1 2 3这样一组数据。(题目中的数据不重复,这里不考虑去重)
解决思路:
第一步:将4 8 1 2 3同时存在 a[]数组 和 b[]数组 里。
第二步:对b[]数组按照从小到大的顺序排序:如下:1 2 3 4 8
第三步:询问a[]数组里的每个数在b[]数组里相对位置(第几大的数)
a[] 4 8 1 2 3
pos: 4 5 1 2 3
因为逆序对的数目和数的大小无关,而是数的相对大小。
第四步:求 4 5 1 2 3 的逆序对数
第五步:(核心的一步)
维护一棵:叶节点数为5的数,数的节点 表示在此区间有多少数。
(1)将4插入到第4个叶节点,查找此时(1~4)区间插入了几个数了,为0,用(1)减去0,就是前面比4大的数有几个,结果为0
(2)将5插入到第5个叶节点,查找此时(1~5)区间插入了几个数了,为2,用(2)减去2,就是前面比5大的数有几个,结果为0
(3)将1插入到第1个叶节点,查找此时(1~1)区间插入了几个数了,为1,用(3)减去1,就是前面比1大的数有几个,结果为2
(4)将2插入到第2个叶节点,查找此时(1~2)区间插入了几个数了,为2,用(4)减去2,就是前面比2大的数有几个,结果为2
(5)将3插入到第3个叶节点,查找此时(1~3)区间插入了几个数了,为3,用(5)减去3,就是前面比3大的数有几个,结果为2
总之,就是用当前 插入的数的总数量 - 插入的数里面不大于这个数的数的数目 == 这个数前面比这个数大的数的数目
<这是线段树的代码,在这个代码后面是树状数组的代码。>
//n < 500,000,0 ≤ a[i] ≤ 999,999,999,
//逆序对+离散化处理
//形成一个1~~n的空树
//m每次加入一个点a[i],就对区间里对应小区间和大区间加1
//然后统计这个点之前的区间里面有几个值,就是有几个小于它的值,然后用总的减去小于的,加上大于的。
//我们不回答[l,r]之间有几个数比h小,我们只需要回答比h小的数中有几个是介于[l,r]之间的
///
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int num=5000010;
int n,a[num],b[num],len;
struct section
{
int L,R,sum;
}d[4*num];
void simplify(void)
{
sort(b+1,b+n+1);
for(int i=1;i<=n;i++)
{
int pos=lower_bound(b+1,b+n,a[i])-b;
a[i]=pos;
}
return;
}
void update_tree(int k)
{
d[k].sum = d[k<<1].sum + d[k<<1|1].sum;
return ;
}
void build_tree(int k,int L,int R)
{
d[k].L=L;d[k].R=R;
d[k].sum=0;
if(L==R) return ;
int mid=(L+R)>>1;
build_tree(k<<1,L,mid);
build_tree(k<<1|1,mid+1,R);
update_tree(k);
return ;
}
//在空树中加入新点:
void in_tree(int k,int L,int R)
{
if(d[k].L==L&&d[k].R==R)
{
d[k].sum+=1;
return ;
}
int mid=(d[k].L+d[k].R)>>1;
if(L<=mid) in_tree(k<<1,L,R);
if(R>mid) in_tree(k<<1|1,L,R);
update_tree(k);
return ;
}
//找到1~~~(x-1)里面有几个数啊
int search_tree(int k ,int L,int R)
{ //L~~R;
if(L <= d[k].L&&d[k].R <= R)
{
return d[k].sum;
}
int mid=(d[k].L+d[k].R)>>1;
int ans=0;
if(L<=mid) ans+=search_tree(k<<1,L,R);
if(R>mid) ans+=search_tree(k<<1|1,L,R);
return ans;
}
int main()
{
long long ans;
while(scanf("%d",&n)&&n)
{
len=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
if(a[i]>len) len=a[i];
}
simplify();
build_tree(1,1,len);
ans=0;
for(int i=1;i<=n;i++)
{
in_tree(1,a[i],a[i]);
ans+=(i-search_tree(1,1,a[i]));
}
printf("%lld\n",ans);
}
return 0;
}
//维护这样一个数组,记录前面几个数据了
//执行这样两种操作,1,插入数据
//2, 询问这段区间之前有几个比它小
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int num=500010;
int c[num],a[num],b[num],n,len;
void int_i(void)
{
for(int i=1;i<=len;i++)
c[i]=0;
return ;
}
void simplify(void)
{
sort(b+1,b+n+1);
len=unique(b+1,b+n+1)-(b+1);
for(int i=1;i<=n;i++)
{
int pos=lower_bound(b+1,b+len+1,a[i]) - b;//这里是len,不是n了,注意
a[i]=pos;
}
return ;
}
int lowbit(int x)
{
return x&(-x);
}
void update_point(int pos,int k)
{
while(pos<=len)
{
c[pos]+=k;
pos=pos+lowbit(pos);
}
}
int getsum(int pos)
{
int ans=0;
while(pos>0)
{
ans+=c[pos];
pos-=lowbit(pos);
}
return ans;
}
int main()
{
long long ans;
while(scanf("%d",&n)&&n)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
simplify();
int_i();
ans=0;
for(int i=1;i<=n;i++)
{
update_point(a[i],1);
ans+=(i-getsum(a[i]));
}
printf("%lld\n",ans);
}
return 0;
}