【Cqoi2011】【BZOJ3295】动态逆序对

48 篇文章 0 订阅
18 篇文章 0 订阅

Description

对于序列A,它的逆序对数定义为满足i

Solution

树状数组

一看到逆序对就要想到树状数组。
维护每个数前面到目前有多少个比自己大。

动态的思路

因为要动态维护,每次只删掉一个数在逆序对中的贡献:及每个数前面有多少个比自己大,每个数后面有多少数比自己小。
先预处理出每个数前面有多少个比自己大ll[i],每个数后面有多少个数比自己小rr[i]。
很显然每次只用总答案删掉位置为i的数,答案就减去ll[i]+rr[i]就可以了,但是有一个问题。
如果已经删掉了一些数在统计进答案里面会重复删减。

套上权值线段树

那么我们再把已经删减的数用一个数据结构来维护,因为要维护每个数前面与后面的答案,考虑前缀和的思路,自然就是树状数组了,然后在范围内查询个数,用权值线段树就可以了。

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long lll;
const int maxn=100007;
int i,j,l,n,m,ll[maxn],rr[maxn],a[maxn],b[maxn];
int tree[maxn],root[maxn],num;
int A[maxn],B[maxn];
lll ans,k;
struct node{
    int l,r,sum;
}t[maxn*50];
int lowbit(int x){
    return (-x)&x;
}
int get(int x){
    int i,j=0;
    for(i=x;i;i-=lowbit(i))j+=tree[i];
    return j;
}
void add(int x){
    int i;
    for(i=x;i<=n;i+=lowbit(i))tree[i]++;
}
void build(int &x,int l,int r,int y){
    if(!x)x=++num;
    t[x].sum++;
    if(l==r)return;
    int mid=(l+r)/2;
    if(y<=mid)build(t[x].l,l,mid,y);
    else build(t[x].r,mid+1,r,y);
}
int zhaoda(int x,int y,int z){
    A[0]=B[0]=0;x--;
    int i,l=1,r=n,mid,k=0;
    for(i=x;i;i-=lowbit(i))A[++A[0]]=root[i];
    for(i=y;i;i-=lowbit(i))B[++B[0]]=root[i];
    while(l<r){
        mid=(l+r)/2;
        if(mid>=z){
            fo(i,1,A[0])k-=t[t[A[i]].r].sum;
            fo(i,1,B[0])k+=t[t[B[i]].r].sum;
            fo(i,1,A[0])A[i]=t[A[i]].l;
            fo(i,1,B[0])B[i]=t[B[i]].l;
            r=mid;
        }
        else{
            fo(i,1,A[0])A[i]=t[A[i]].r;
            fo(i,1,B[0])B[i]=t[B[i]].r;
            l=mid+1;
        }
    }
    return k;
}
int zhaoxiao(int x,int y,int z){
    A[0]=B[0]=0;x--;
    int i,l=1,r=n,mid,k=0;
    for(i=x;i;i-=lowbit(i))A[++A[0]]=root[i];
    for(i=y;i;i-=lowbit(i))B[++B[0]]=root[i];
    while(l<r){
        mid=(l+r)/2;
        if(mid<z){
            fo(i,1,A[0])k-=t[t[A[i]].l].sum;
            fo(i,1,B[0])k+=t[t[B[i]].l].sum;
            fo(i,1,A[0])A[i]=t[A[i]].r;
            fo(i,1,B[0])B[i]=t[B[i]].r;
            l=mid+1;
        }
        else{
            fo(i,1,A[0])A[i]=t[A[i]].l;
            fo(i,1,B[0])B[i]=t[B[i]].l;
            r=mid;
        }
    }
    return k;
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    fo(i,1,n){
        scanf("%lld",&a[i]);
        b[a[i]]=i;
        ll[i]=get(n)-get(a[i]);
        ans+=ll[i];
        add(a[i]);
    }
    memset(tree,0,sizeof(tree));
    fod(i,n,1){
        rr[i]=get(a[i]-1);
        add(a[i]);
    }
    fo(i,1,m){
        printf("%lld\n",ans);
        scanf("%lld",&k);
        ans-=(ll[b[k]]+rr[b[k]]-zhaoda(1,b[k]-1,k)-zhaoxiao(b[k]+1,n,k));
        for(j=b[k];j<=n;j+=lowbit(j))build(root[j],1,n,k); 
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值