pku2299 Ultra-QuickSort
题目意思是求一个序列的逆序数,朴素的做法时间复杂度是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 <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;
}
#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;
}