猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai>aj 且 i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
Update:数据已加强。
第一行,一个数 n,表示序列中有 n个数。
第二行 n 个数,表示给定的序列。序列中每个数字不超过 109。
输出序列中逆序对的数目。
6
5 4 2 6 3 1
11
对于 25% 的数据,n≤2500
对于 50% 的数据,n≤4×104。
对于所有数据,n≤5×105
请使用较快的输入输出
应该不会 O(n2) 过 50 万吧 by chen_zhe :)
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long tree[10000001],tag[10000001],a[10000001];
int ls(int x){return x*2;}
int rs(int x){return x*2+1;}
void up(int ix){tree[ix]=tree[ls(ix)]+tree[rs(ix)];}
struct node{int x,ix,l;}p[10000001];
int cmp1(node p1,node p2){return p1.x<p2.x;}
int cmp2(node p1,node p2){return p1.ix<p2.ix;}
void downp(int l,int r,int ix){
if(tag[ix]){
tag[ls(ix)]+=tag[ix];
tag[rs(ix)]+=tag[ix];
int mid=(l+r)/2;
tree[ls(ix)]+=tag[ix]*(mid-l+1);
tree[rs(ix)]+=tag[ix]*(r-mid);
tag[ix]=0;
}
return;
}
void build(int l,int r,int ix){
if(l==r){
tree[ix]=a[l];
return;
}
int mid=(l+r)/2;
build(l,mid,ls(ix));
build(mid+1,r,rs(ix));
up(ix);
}
void change(int l,int r,int x,int y,int ix,int k){
if(x<=l&&r<=y){
tree[ix]+=k*(r-l+1);
tag[ix]+=k;
return;
}
downp(l,r,ix);
int mid=(l+r)/2;
if(x<=mid)
change(l,mid,x,y,ls(ix),k);
if(y>mid)
change(mid+1,r,x,y,rs(ix),k);
up(ix);
}
long long query(int l,int r,int x,int y,int ix){
if(x<=l&&r<=y){
return tree[ix];
}
downp(l,r,ix);
int mid=(l+r)/2;
long long ans=0;
if(x<=mid)
ans+=query(l,mid,x,y,ls(ix));
if(mid<y)
ans+=query(mid+1,r,x,y,rs(ix));
return ans;
}
int main(){
cin>>n;
build(1,n,1);
for(int i=1;i<=n;i++){
scanf("%d",&p[i].x);
p[i].ix=i;
}
sort(p+1,p+1+n,cmp1);
int o=1;
for(int i=1;i<=n;i++){
if(p[i].x==p[i-1].x){
p[i].l=o;
continue;
}
o=i;
p[i].l=o;
}
sort(p+1,p+n+1,cmp2);
long long ans=0;
for(int i=1;i<=n;i++){
a[i]=p[i].l;
change(1,n,a[i],a[i],1,1);
if(a[i]+1<=n){
ans+=query(1,n,a[i]+1,n,1);
}
}
cout<<ans;
return 0;
}
线段树