【数据结构】(树套树)BZOJ3295动态逆序对

题意:给出一个数列,依次删除其中m个,求每一个数字删去后,剩余数列有多少个逆序对


分析:
很直观很暴力的想法,树套树:外层线段树,内层平衡树。
在线段树每个节点建一颗平衡树,平衡树中有这个范围内存在(未被删去)的数。
为节约时间,从后往前做加入操作(离线)。
每次删除(查询)ai,在已有的ans上,加入在1—i-1内找比ai大的数,以及i+1—n内找比ai小的数的总数

注意:
这是暴力数据结构的方法,时间为O(nlog²n)。常数也相当大,估计得卡一会儿。。
平衡树建议使用treap,splay常数过大

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define SF scanf
#define PF printf
#define INF 0x7FFFFFFF
#define MAXN 100010
#define LUCKY_NUM 20020214
using namespace std;
int summ,summ1;
int tot=1,a[MAXN],b[MAXN],se[MAXN],vis[MAXN];
struct node{
    node *ch[2];
    int r,v,num,num1;
    node (int v):v(v) {
        num=1;
        num1=1;
        ch[0]=ch[1]=NULL;
        r=rand();
    }
    bool operator < (const node &rhs) const{
        return r<rhs.r;
    }
    int cmp (int x) const{
        if(x==v) return -1;
        return x < v ? 0 : 1;
    }
};
node * Root[MAXN*4];
int maxi,mini;
void rotate(node * &o,int d){
    node *k=o->ch[!d];
    o->ch[!d]=k->ch[d];
    k->ch[d]=o;
    o=k;
    node * o1=o->ch[d];
    o1->num1=o1->num;
    if(o1->ch[0]!=NULL)
        o1->num1+=o1->ch[0]->num1;
    if(o1->ch[1]!=NULL)
        o1->num1+=o1->ch[1]->num1;
    o->ch[d]=o1;
    o->num1=o->num;
    if(o->ch[0]!=NULL)
        o->num1+=o->ch[0]->num1;
    if(o->ch[1]!=NULL)
        o->num1+=o->ch[1]->num1;

}
int find(node * o, int x){
    while(o != NULL)
    {
        int d = o->cmp(x);
        if(d == -1) return 1;
        else o = o->ch[d];
    }
    return 0;
}
void insert(node * &o,int x){
    if(o==NULL)
        o=new node(x);
    else{
        int d=o->cmp(x);
        if(d==-1){
            o->num++;
            o->num1++;
            return ;
        }
        o->num1++;
        insert(o->ch[d],x);
        if(o->ch[d]->r > o->r)
            rotate(o,!d);
    }
}
void dele(node * &o,int x){
    if(o==NULL) return ;
    if(o->v==x){
        if(o->num>=2){
            o->num1--;
            o->num--;
            return ;
        }
        if(o->ch[0]==NULL||o->ch[1]==NULL){
            if(o->ch[0]==NULL)
                o=o->ch[1];
            else
                o=o->ch[0];
        }
        else if(o->ch[0]->r < o->ch[1]->r){
            rotate(o,1);
            dele(o,x);
        }
        else{
            rotate(o,0);
            dele(o,x);
        }
    }
    else if(x> o->v){
        o->num1--;
        dele(o->ch[1],x);
    }
    else{
        o->num1--;
        dele(o->ch[0],x);
    }
}
int ask3(node * o,int x){
    if(o==NULL)
        return 0;
    int d=o->cmp(x);
    if(d==-1){
        if(o->ch[0]==NULL)
            return 1;
        return o->ch[0]->num1 + 1;
    }
    if(d==0)
        return ask3(o->ch[d],x);
    else{
        if(o->ch[0]==NULL)
            return o->num+ask3(o->ch[d],x);
        return o->ch[!d]->num1 + o->num + ask3(o->ch[d],x);
    }
}
struct Tree{
    int pl,pr,id,l,r;
}t[MAXN*4];
void build(int id,int l,int r){
    t[id].l=l;
    t[id].r=r;
    if(l==r)
        return ;
    int mid=(l+r)/2;
    tot++;
    t[id].pl=tot;
    build(tot,l,mid);
    tot++;
    t[id].pr=tot;
    build(tot,mid+1,r);
}
void intree(int id,int x){
    int y=se[x];
    int mid=(t[id].l+t[id].r)/2;
    insert(Root[id],x);
    if(t[id].l==t[id].r)
        return ;
    if(mid>=se[x])
        intree(t[id].pl,x);
    else
        intree(t[id].pr,x);
}
long long nx(int id,int x){
    int y=se[x];
    long long res=0;
    if(t[id].r<y){
        if(Root[id]==NULL)
            res=0;
        else
            res=Root[id]->num1-ask3(Root[id],x);
        return res;
    }
    else if(t[id].l>y){
        res=ask3(Root[id],x);
        return res;
    }
    if(t[id].pl!=0)
        res+=nx(t[id].pl,x);
    if(t[id].pr!=0)
        res+=nx(t[id].pr,x);
    return res;
}
int n,m;
long long ans,ans1[MAXN];
int main(){
    //freopen("inverse.in","r",stdin);
    //freopen("inverse.out","w",stdout);
    SF("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        SF("%d",&a[i]);
        se[a[i]]=i;
    }
    for(int i=1;i<=m;i++){
        SF("%d",&b[i]);
        vis[b[i]]=1;
    }
    build(1,1,n);
    for(int i=1;i<=n;i++){
        if(vis[a[i]]==0){
            intree(1,a[i]);
            ans+=nx(1,a[i]);
        }
    }
    for(int i=m;i>=1;i--){
        intree(1,b[i]);
        ans+=nx(1,b[i]);
        ans1[i]=ans;
    }
    for(int i=1;i<=m;i++)
        PF("%lld\n",ans1[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值