对着标签<线段树>找的,结果一看是逆序对,直接用归并排序水过......
线段树也能做这道题,不过没必要 : D ,但是本蒟蒻想试试
哈希:线性读入a[i],查询到a[1]~a[i-1]有多少个比a[i]大,累计输出。
即查询在此之前具体数值a[i]+1~max{a[1]~a[n]},出现过了几个。
比如1 2 3 4,读入到2的时候,去查询[3,4]是否出现过。
所以可以用bool数组给出现过的数打上标记,直接对bool数组对应区间求和。
但是这道题a[i]最大有maxlongint=2^32这么大,bool flag[2^32]就有4096MB,直接爆空间了。
离散化:根据题目可以知道,只需要数值之间的大小关系,{1,2}和{1,100},并不会影响最终的结果,所以只取对应的大小关系,将庞大的数值映射到有限的空间中,称之为离散化。
如{4,8,0,3}->{3,4,1,2},因为最多有1e4个数,所以最大值就离散成了1e4
线段树:用树状数组也可以,目的在于对 flag数组的区间 a[i]+1~max{a[1]~a[n]} 进行快速求和。
AC代码
#include
using namespace std;
using ll=long long;
const int N=500001;
int n,a[N],b[N],rk[N],rnk;
ll ans;
struct {
ll sum;
}t[N<<2];
int find(int x){
int l=1,r=rnk;
while(l!=r){
int mid=l+r>>1;
if(x>rk[mid])l=mid+1;
else r=mid;
}
return l;
}
inline void betterCinCout(){//加速cin,cout
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
}
inline void input(){//题面输入
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i];
}
inline void discrete(){//离散化
for(int i=1;i<=n;++i)//复制a数组
b[i]=a[i];
sort(b+1,b+1+n);//排序
for(int i=1;i<=n;++i)//给排序后的数组按顺序进行标记,不能重复标记.
if(i==1||b[i]!=b[i-1])
rk[++rnk]=b[i];
for(int i=1;i<=n;++i)//将数组a更新成离散后的大小关系
a[i]= find(a[i]);//在rk中找到a[i]
} //若rk[j]=a[i],则j为a[i]离散化之后的大小关系
void insert(int l,int r,int i,int pos){
if(l==r){
if(l==pos)t[i].sum+=1;
return ;
}
int mid=l+r>>1;
if(pos<=mid)insert(l,mid,i<<1,pos);
if(pos>mid)insert(mid+1,r,i<<1|1,pos);
t[i].sum=t[i<<1].sum+t[i<<1|1].sum;
}
int query(int l,int r,int i,int x,int y){
if(l>=x and r<=y)return t[i].sum;
ll res=0;
int mid=l+r>>1;
if(x<=mid)res+= query(l,mid,i<<1,x,y);
if(y>mid)res+=query(mid+1,r,i<<1|1,x,y);
return res;
}
int main(){
betterCinCout();
input();
discrete();
for(int i=1;i<=n;++i){
insert(1,n,1,a[i]);//在对应位置插入a[i]
ans+= query(1,n,1,a[i]+1,n);//查询区间和[a[i]+1,n]
}
cout<
挺慢的,而且代码也不好写,真不如归并排序
查询的时候不需要开long long,最多5e5个数,即每次查询的最大值只有5e5-1,只有累计答案才需要开long long(我管你那么多)