您的位置:首页 > 产品设计 > UI/UE

POJ[2299]Ultra-QuickSort 逆序对:线段树||树状数组||分治

2017-09-06 22:02 615 查看
题目链接:http://poj.org/problem?id=2299

题目大意:给n个数(n≤50000),求逆序对数

求逆序对是一种经典题型,今天整理一下(闲的蛋疼)各种写法的逆序对…

复杂度都是O(nlogn)

树状数组的思想就是对于每个数找出之前有k个数比他大,这个k就是对答案的贡献

可以用开个树状数组a[i]表示1~i有多少个数,则比i大的数一共有i-Sum(i)+1(后插入i)个数(也可以倒着开…)

优点:思维复杂度低 缺点:代码比较长,数据过大时要离散化,常数比分治(归并排序)略大

线段树的思想和树状数组差不多

记录答案只需要查询Sum(i+1,MAXN)即可…

优点:更好消磨时间 缺点:代码长,常数大,容易MLE,也要离散化

分治(归并排序)可能是最优的写法了吧…

和正常分治的思路一样,先处理左右区间(对他们从小到大排序)然后找出某时a[i]>a[j],则i的贡献就是mid-i+1

优点:代码短 常数小 缺点:递归层数深时容易崩溃..

不同算法的效率比较(从上至下依次为 线段树 树状数组 分治):



线段树代码:

#include<algorithm>
#include<cstring>
#include<ctype.h>
#include<cstdio>
using namespace std;
int n,x,tmp,maxn,cnt,top=1;
long long ans;
inline int max(int a,int b) {return a>b?a:b;}
int a[500020],b[500020];
struct Seg{
int sum,l,r;
Seg *ls,*rs;
}q[2000020];
inline int read(){
char c=getchar();
int x=0,f=1;
while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*f;
}
bool cmp(int a,int b){return a<b;}
void maketree(int l,int r,Seg *k){
k->l=l;k->r=r;
if(l==r){
k->sum=0;
return;
}
k->ls=&q[++top];k->rs=&q[++top];
int mid=(l+r)>>1;
maketree(l,mid,k->ls);maketree(mid+1,r,k->rs);
k->sum=k->ls->sum+k->rs->sum;
}
int Query_sum(int x,int y,Seg *k){
if(k->l>=x && k->r<=y) return k->sum;
int mid=(k->l+k->r)>>1;
if(mid>=y) return Query_sum(x,y,k->ls);
if(mid<x) return Query_sum(x,y,k->rs);
return Query_sum(x,y,k->ls)+Query_sum(x,y,k->rs);
}
void add(int x,int v,Seg *k){
if(k->l==k->r){
k->sum=v;
return;
}
int mid=(k->l+k->r)>>1;
if(x<=mid) add(x,v,k->ls);
else add(x,v,k->rs);
k->sum=k->ls->sum+k->rs->sum;
}
main(){
maketree(1,500020,&q[1]);
while(""){
for(int i=1;i<=2000000;i++) q[i].sum=0;
ans=0;
n=read();
if(!n) break;
for(int i=1;i<=n;i++)
b[i]=a[i]=read();
sort(b+1,b+n+1,cmp);
cnt=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
x=lower_bound(b+1,b+cnt+1,a[i])-b;
ans=ans+Query_sum(x+1,500000,&q[1]);
add(x,1,&q[1]);
}
printf("%lld\n",ans);
}
return 0;
}


树状数组代码:

#include<algorithm>
#include<cstring>
#include<ctype.h>
#include<cstdio>
#define int long long
using namespace std;
int n,x,tmp,maxn,cnt;
long long ans;
inline int max(int a,int b) {return a>b?a:b;}
int s[500020],a[500020],b[500020];
inline int read(){
char c=getchar();
int x=0,f=1;
while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*f;
}
inline int lowbit(int x){return x&-x;}
inline int sum(int x){
for(tmp=0;x;x=x-(x&-x)) tmp=tmp+s[x];
return tmp;
}
inline int add(int x,int v){
for(;x<=500000;x=x+(x&-x)) s[x]=s[x]+v;
}
bool cmp(int a,int b){return a<b;}
main(){
while(""){
ans=0;
n=read();
if(!n) break;
memset(s,0,sizeof s);
for(int i=1;i<=n;i++)
b[i]=a[i]=read();
sort(b+1,b+n+1,cmp);
cnt=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++){
x=lower_bound(b+1,b+cnt+1,a[i])-b;
ans=ans+i-sum(x)-1;
add(x,1);
}
printf("%lld\n",ans);
}
return 0;
}


归并排序算法:

#include<algorithm>
#include<cstdio>
#define int long long
using namespace std;
int n,ans;
int a[500020],s[500020];
void calc(int l,int r){
if(l==r) return;
int mid=(l+r)>>1,p=l,q=mid+1,top=l;
calc(l,mid);calc(mid+1,r);
while(p<=mid||q<=r){
if(q>r||(p<=mid&&a[p]<=a[q]))s[top++]=a[p++];
else {s[top++]=a[q++];ans=ans+mid+1-p;}
}
for(int i=l;i<=r;i++) a[i]=s[i];
}
main(){
while(""){
ans=0;
scanf("%lld",&n);
if(!n) break;
for(int i=1;i<=n;i++)
scanf("%lld",a+i);
calc(1,n);
printf("%lld\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: