洛谷 P1774 最接近神的人#哈希+离散化+线段树


对着标签<线段树>找的,结果一看是逆序对,直接用归并排序水过...... 

线段树也能做这道题,不过没必要  : 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 <bits/stdc++.h>
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<<ans;
    return 0;
}

挺慢的,而且代码也不好写,真不如归并排序 

查询的时候不需要开long long,最多5e5个数,即每次查询的最大值只有5e5-1,只有累计答案才需要开long long(我管你那么多

猜你喜欢

转载自blog.csdn.net/qq_17807067/article/details/129987050