[BZOJ3333]排队计划(离散化+树状数组+线段树)

题目描述

传送门

题解

离散化。记高度为h。
首先用树状树组求出逆序对即为第一问的答案。
求出每个点后面有几个比它小记为rev[i],即为它对总逆序对数的贡献。建立线段树维护区间h的最小值。
对于每次询问的点P,不停地找区间[P,n]的最小值i,然后将h[i]赋为正无穷,直到点P的h为最小值停止。每次找到最小值的点都将ans减去那个点的rev。
这样做的基础是基于一个非常有用的性质:逆序对数只会不断减少,因为对于每一次操作,对于P前面的点没有影响,对于它后面的比P大的点也没有影响,而对于后面比它小的点,只有他们互相之间是有影响的,这个影响就是rev都变为0,即不会再产生后面的比它小的数了。
由于每个点只会被修改一次,那么均摊时间复杂度为O(nlogn)。

代码

#include
#include
#include
#include
using namespace std;
#define LL long long
const int max_n=5e5+5;
const int max_tree=max_n*4;
const int INF=2e9;

int n,m,x,y,cnt,a[max_n],p[max_n],h[max_n];
LL rev[max_n],ans,C[max_n];
int tree[max_tree];

inline int cmp(int x,int y){return a[x]inline void Bit_add(int loc,int val){
    for (int i=loc;i<=cnt;i+=i&(-i)) C[i]+=val;
}
inline LL Bit_query(int loc){
    LL ans=0;
    for (int i=loc;i>=1;i-=i&(-i)) ans+=(LL)C[i];
    return ans;
}
inline void Segtree_update(int now){
    if (h[tree[now<<1]]1|1]]) tree[now]=tree[now<<1];
    else tree[now]=tree[now<<1|1];
}
inline void Segtree_build(int now,int l,int r){
    int mid=(l+r)>>1;
    if (l==r){
        tree[now]=l;
        return;
    }
    Segtree_build(now<<1,l,mid);
    Segtree_build(now<<1|1,mid+1,r);
    Segtree_update(now);
}
inline void Segtree_change(int now,int l,int r,int x){
    int mid=(l+r)>>1;
    if (l==r){
        tree[now]=l;
        return;
    }
    if (x<=mid) Segtree_change(now<<1,l,mid,x);
    else Segtree_change(now<<1|1,mid+1,r,x);
    Segtree_update(now);
}
inline int Segtree_query(int now,int l,int r,int lrange,int rrange){
    int mid=(l+r)>>1,ans=INF,anst;
    if (lrange<=l&&r<=rrange) return tree[now];
    if (lrange<=mid){
        int t=Segtree_query(now<<1,l,mid,lrange,rrange);
        if (ans>=h[t]) ans=h[t],anst=t;
    }
    if (mid+1<=rrange){
        int t=Segtree_query(now<<1|1,mid+1,r,lrange,rrange);
        if (ans>=h[t]) ans=h[t],anst=t;
    }
    return anst;
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) scanf("%d",&a[i]),p[i]=i;
    sort(p+1,p+n+1,cmp);
    for (int i=1;i<=n;++i)
      if (a[p[i]]!=a[p[i-1]]) h[p[i]]=++cnt; else h[p[i]]=cnt;
    for (int i=n;i>=1;--i)
      rev[i]=Bit_query(h[i]-1),ans+=rev[i],Bit_add(h[i],1);
    printf("%lld\n",ans);
    Segtree_build(1,1,n);
    for (int i=1;i<=m;++i){
        scanf("%d",&x);
        while (h[x]!=INF){
            y=Segtree_query(1,1,n,x,n);
            h[y]=INF;
            ans-=rev[y];
            Segtree_change(1,1,n,y);
        }
        printf("%lld\n",ans);
    }
}

总结

刚开始的思路好像有点对,但是想了一个树套树233

你可能感兴趣的:(题解,线段树,bit)