BZOJ3295 CQOI2011 动态逆序对 树状数组套线段树

题意:给1到n的一个排列,按照某种顺序依次删除m个元素,每次删除一个元素之前统计整个序列的逆序对数。

题解:

离线倒着做,每次加入一个节点后新增的逆序对数量就是其左边大于它的数的个数(左边数的总数-左边小于它的数的个数)+右边小于它的数的个数

用树状数组维护求和,对于树状数组中每个节点v所对应的区间线段树维护区间[l,r]中大于v的数的个数。

最后唯一的问题就是指针版线段树MLE……

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define lowbit(x) (x&(-x))
#define ll long long

const int MAXN=100000+2;
const int MAXM=50000+2;
typedef struct NODE{
    int l,r;
    ll c;
    NODE *lchild,*rchild;
    NODE(){}
    NODE(int _l,int _r):l(_l),r(_r),c(0),lchild(0),rchild(0){}
} *TREE;
TREE root[MAXN];
int N,M,a[MAXN],p[MAXN],q[MAXM];
ll bit[MAXN],ans[MAXM];
bool flag[MAXN];

void Update(int x){
    while(x<=N) bit[x]++,x+=lowbit(x);
}

ll Summation(int x){
    ll ret=0;
    while(x) ret+=bit[x],x-=lowbit(x);
    return ret;
}

void Pushup(TREE &x){
    x->c=0;
    if(x->lchild) x->c+=x->lchild->c;
    if(x->rchild) x->c+=x->rchild->c;
}

void Add(TREE &x,int l,int r,int p){
    if(!x) x=new NODE(l,r);
    if(l==r){
        x->c++;
        return;
    }

    int m=(l+r)>>1;
    if(p<=m) Add(x->lchild,l,m,p);
    else Add(x->rchild,m+1,r,p);

    Pushup(x);
}

void Insert(int p,int v){
    v++;
    while(v<=N) Add(root[v],1,N,p),v+=lowbit(v);
}

ll Calc(TREE &x,int l,int r){
    if(!x) return 0;
    if(x->l>=l && x->r<=r) return x->c;

    int m=(x->l+x->r)>>1;
    ll ret=0;
    if(l<=m) ret+=Calc(x->lchild,l,r);
    if(r>m) ret+=Calc(x->rchild,l,r);

    return ret;
}

ll Query(int l,int r,int v){
    ll ret=0;
    while(v) ret+=Calc(root[v],l,r),v-=lowbit(v);
    return ret;
}

int main(){
    scanf("%d %d",&N,&M);
    for(int i=1;i<=N;i++){
        scanf("%d",a+i);
        p[a[i]]=i;
    }
    for(int i=1;i<=M;i++){
        scanf("%d",q+i);
        q[i]=p[q[i]],flag[q[i]]=1;
    }

    for(int i=1;i<=N;i++)
        if(!flag[i]){
            Update(a[i]),ans[M]+=Summation(N)-Summation(a[i]);
            Insert(i,a[i]);
        }

    memset(bit,0,sizeof(bit));

    for(int i=1;i<=N;i++)
        if(!flag[i]) Update(i);

    for(int i=M;i;i--){
        Insert(q[i],a[q[i]]);
        Update(q[i]);
        ans[i-1]=ans[i]+Summation(q[i]-1)-Query(1,q[i]-1,a[q[i]])+Query(q[i]+1,N,a[q[i]]);
    }

    for(int i=0;i<M;i++) printf("%lld\n",ans[i]);

    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/WDZRMPCBIT/p/6444310.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值