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

来源:互联网 发布:桶装水管理软件 编辑:程序博客网 时间:2024/05/16 03:06

题目描述

传送门

题解

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

代码

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define LL long longconst 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]<a[y];}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]]<h[tree[now<<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

0 0
原创粉丝点击