P3157 [CQOI2011]动态逆序对(树状数组套线段树)

P3157 [CQOI2011]动态逆序对

树状数组套线段树

静态逆序对咋做?树状数组(别管归并QWQ)

然鹅动态的咋做?

我们考虑每次删除一个元素。

减去的就是与这个元素有关的逆序对数,介个可以预处理:从左到右求一次,再倒过来求一次,用2个数组存起来。

但是前面已经删除的元素与当前删除元素组成的逆序对会被重复计数。

于是考虑再减去重复计数

我们用树状数组套线段树(动态开点):

第$i$棵线段树 储存 每个位置在$i$之前的被删除元素

蓝后每次查询时左边右边找一找

把它们加回来就好辣

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
void read(int &x){
    static char c=getchar();x=0;
    while(c<'0'||c>'9') c=getchar();
    while('0'<=c&&c<='9') x=x*10+(c^48),c=getchar();
}
#define W 6000005
#define N 100005
int n,m,u,pos[N],id[N],s[N];
int rt[N],sum[W],lc[W],rc[W];
ll ans,L[N],R[N];
void T_Add(int x,int v){for(;x<=n;x+=x&-x)s[x]+=v;}
int T_Sum(int x){int re=0; for(;x;x-=x&-x)re+=s[x]; return re;}
#define mid (l+r)/2
void S_Add(int &o,int l,int r,int x){
    if(!o) o=++u;
    ++sum[o];
    if(l==r) return;
    if(x<=mid) S_Add(lc[o],l,mid,x);
    else S_Add(rc[o],mid+1,r,x);
}
int S_Sum(int o,int l,int r,int x1,int x2){
    if(x1<=l&&r<=x2) return sum[o];
    int re=0;
    if(x1<=mid) re+=S_Sum(lc[o],l,mid,x1,x2);
    if(x2>mid) re+=S_Sum(rc[o],mid+1,r,x1,x2);
    return re;
}
ll S_Find(int l,int r,int x1,int x2){//查询l+1~r内所有范围在x1~x2的个数
    if(x1>x2) return 0;
    ll re=0;
    for(int i=r;i;i-=i&-i) re+=(ll)S_Sum(rt[i],1,n,x1,x2);
    for(int i=l;i;i-=i&-i) re-=(ll)S_Sum(rt[i],1,n,x1,x2);
    return re;
}
int main(){
    read(n);read(m); register int i,j; int q,p;
    for(i=1;i<=n;++i){
        read(pos[i]); id[pos[i]]=i;
        L[i]=T_Sum(n)-T_Sum(pos[i]);
        ans+=L[i]; T_Add(pos[i],1);
    }memset(s,0,sizeof(s));
    for(i=n;i;--i) R[i]=T_Sum(pos[i]-1),T_Add(pos[i],1);
    for(i=1;i<=m;++i){
        printf("%lld
",ans); read(q); p=id[q];
        ans-=L[p]+R[p]-S_Find(0,p,q+1,n)-S_Find(p,n,1,q-1);
        for(j=p;j<=n;j+=j&-j) S_Add(rt[j],1,n,q);
    }return 0;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/10586547.html