题目意思是求一个序列的逆序数,朴素的做法时间复杂度是O(n^2),其中 n < 500,000 ,结果不用说肯定是超时的;于是思路转向了O(nlogn)的算法,换言之离不开二分、树型结构等方法
这里用到的是杨挺的PDF《树状数组和线段树》里提到的方法:巧妙地将问题转化成类似求RMQ的问题,然后通过树状树组解决
1,将序列离散化,即序列内第k小的元素变成k,注意到题目已说明序列内无相同元素;离散化可通过排序和二分查找解决
2,将b[]初始化为0,遍历离散化后的序列a[1~n],每当向树状数组c[]插入a[i]前,根据c[]用O(logn)的时间求b[1]~b[a[i]]的和,即求得比a[i]先插入且比a[i]小的元素个数(放到序列上理解,就是排在a[i]前面且比a[i]小的元素个数),换算后可得比a[i]先插入且比a[i]大的元素个数,在遍历过程中将其累加到ans,即为答案
3,更新ans后置b[a[i]]为1,同时更新c[]的值(对c[]插入a[i])
排序和统计的复杂度都为O(nlogn),所以算法总复杂度是O(nlogn)
#include
<
iostream
>
#include
<
algorithm
>
using
namespace
std;
#define
MAXN 500005
#define
clr(x) memset(x,0,sizeof(x))
int
a[MAXN],b[MAXN],c[MAXN],n;
__int64 ans;
int
find(
int
x){
int
l
=
1
,r
=
n,m;
while
(r
>=
l){
m
=
(l
+
r)
/
2
;
if
(x
<
b[m])
r
=
m
-
1
;
else
if
(x
>
b[m])
l
=
m
+
1
;
else
return
m;
}
return
-
1
;
}
void
discretization(){
int
i,t;
for
(i
=
1
;i
<=
n;i
++
){
t
=
find(a[i]);
a[i]
=
t;
}
}
inline
int
lowbit(
int
x){
return
x
&
(
-
x);
}
int
sum(
int
k){
//
求sum(b[1]~b[k])
int
i,res
=
0
;
for
(i
=
k;i
>
0
;i
-=
lowbit(i))
res
+=
c[i];
return
res;
}
void
improve(
int
k){
//
插入a[i]后b[a[i]]的值有修改
int
i;
//
若对b[k]有修改,设影响到了c[p1]、c[p2]、 ,则p1=k,pi+1=pi + lowbit(pi)
for
(i
=
k;i
<=
n;i
+=
lowbit(i))
c[i]
++
;
}
void
build(){
//
建立树状数组c[]
clr(b);
clr(c);
int
i,k;
for
(i
=
1
;i
<=
n;i
++
){
k
=
a[i];
ans
+=
i
-
sum(k)
-
1
;
b[k]
=
1
;
improve(k);
}
}
int
main(){
int
i;
while
(scanf(
"
%d
"
,
&
n)
&&
n){
for
(i
=
1
;i
<=
n;i
++
){
scanf(
"
%d
"
,a
+
i);
b[i]
=
a[i];
}
sort(b
+
1
,b
+
n
+
1
);
discretization();
//
离散化
ans
=
0
;
build();
printf(
"
%I64d\n
"
,ans);
}
return
0
;
}
另附合并排序求逆序数代码:
#include
<
iostream
>
#include
<
algorithm
>
using
namespace
std;
#define
MAXN 500001
int
a[MAXN],t[MAXN],n;
__int64 ans;
void
Merge(
int
l,
int
m,
int
r){
int
i
=
0
,j
=
l,k
=
m
+
1
;
while
(j
<=
m
&&
k
<=
r){
if
(a[j]
>
a[k]){
t[i
++
]
=
a[k
++
];
ans
+=
m
-
j
+
1
;
}
else
t[i
++
]
=
a[j
++
];
}
while
(j
<=
m)
t[i
++
]
=
a[j
++
];
while
(k
<=
r)
t[i
++
]
=
a[k
++
];
for
(j
=
0
;j
<
i;j
++
){
a[l
+
j]
=
t[j];
}
}
void
Mergesort(
int
l,
int
r){
if
(l
<
r){
int
m
=
(l
+
r)
/
2
;
Mergesort(l,m);
Mergesort(m
+
1
,r);
Merge(l,m,r);
}
}
int
main(){
int
i;
while
(scanf(
"
%d
"
,
&
n)
&&
n){
ans
=
0
;
for
(i
=
0
;i
<
n;i
++
)
scanf(
"
%d
"
,a
+
i);
Mergesort(
0
,n
-
1
);
printf(
"
%I64d\n
"
,ans);
}
return
0
;
}