动态逆序对,Dynamic Inversion,Uva11990(树套树)

本文介绍了一种针对排列A的逆序对计数算法,该算法能够在删除元素的同时高效地更新逆序对的数量。使用了Treap数据结构来维护元素的位置信息,并通过线段树辅助实现快速查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给一个1~n的排列A,要求按照某种顺序删除一些数(其他数顺序不变),输出每次删除之前逆序对的数目。所谓逆序对数,就是满足i<j且A[i]>A[j]的有序对(i,j)的数目



首先用O(nlogn)时间求出初始排列的逆序对数,则每次需要求出被删除的那个数左边有多少个数比它大,右边有多少个数比它小。


TLE的代码,忧伤。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define FORD(i,a,b) for(int i=(a);i<=(b);++i)
const int maxn=200010;
using namespace std;
int n,m;
int arr[maxn],pos[maxn];
int res;
struct Node{
    Node* ch[2];
    int v,r,s;
};
Node* null=new Node();
struct Treap{
    Node* root;
    void init(){root=null;}
    void push_up(Node* rt){
        rt->s=rt->ch[0]->s+rt->ch[1]->s+1;
    }
    void rotate(Node* &o,int d){
        Node* k=o->ch[d^1];
        o->ch[d^1]=k->ch[d];
        k->ch[d]=o;
        push_up(o);
        push_up(k);
        o=k;
    }
    void insert(Node* &o,int x){
        if(o==null){
            o=new Node();
            o->ch[0]=o->ch[1]=null;
            o->s=1;
            o->r=rand();
            o->v=x;
        }else{
            int d=(o->v>x?0:1);
            insert(o->ch[d],x);
            if(o->ch[d]->r>o->r) rotate(o,d^1);
        }
        push_up(o);
    }
    void remove(Node* &o,int x){
        if(o==null) return;
        int d=(o->v>x?0:1);
        if(o->v==x){
            Node* u=o;
            if(o->ch[0]!=null&o->ch[1]!=null){
                int d2=(o->ch[0]->r>o->ch[1]->r?1:0);
                rotate(o,d2);remove(o->ch[d],x);

            }else{
                if(o->ch[0]==null) o=o->ch[1];
                else o=o->ch[0];
                delete(u);
            }
        }else remove(o->ch[d],x);
        if(o!=null) push_up(o);
    }
    int findk(Node* o,int x){
        if(o==null) return 0;
        if(o->v==x) return o->ch[0]->s+1;
        else if(o->v>x) return findk(o->ch[0],x);
        else return o->ch[0]->s+1+findk(o->ch[1],x);
    }
};
struct node{
    Treap tree;
    void init(){tree.init();}
    int get(int x){
        return tree.findk(tree.root,x);
    }
    void update(int x){
        if(x>0) tree.insert(tree.root,x);
        else tree.remove(tree.root,-x);
    }
}s[maxn];
struct IndexTree{
    int c[maxn];
    void init(){memset(c,0,sizeof(c));}
    void update(int x,int v){
        for(int i=x;i<=n;i+=i&-i) c[i]+=v;
    }
    int get_sum(int x){
        int sum=0;
        for(int i=x;i>0;i-=i&-i) sum+=c[i];
        return sum;
    }
}all,now;

void inc(int p,int v){
    for(int i=p;i<=n;i+=i&-i) s[i].update(v);
}
int query(int x,int v){
    int ans=0;
    for(int i=x;i>0;i-=i&-i){
        ans+=s[i].get(v);
    }
    return ans;
}

void init(){
    res=0;
    all.init();
    now.init();
    FORD(i,1,n) s[i].init();
}
void build(){
    init();
    FORD(i,1,n){
        scanf("%d",&arr[i]);
        pos[arr[i]]=i;
        res+=i-all.get_sum(arr[i])-1;
        all.update(arr[i],1);
        now.update(i,1);
        inc(i,arr[i]);
    }
}
void solve(int x){
    int k=now.get_sum(pos[x]-1);
    int a=all.get_sum(x-1);
    int b=query(pos[x],x-1);
    all.update(x,-1);
    now.update(pos[x],-1);
    inc(pos[x],-x);
//    cout<<"a="<<a<<" b="<<b<<" k="<<k<<endl;
//    cout<<"disinc="<<k-b+a-b<<endl;
    res=res-(k-b+a-b);
}
int main()
{
//#ifndef ONLINE_JUDGE
//freopen("in.cpp","r",stdin);
//#endif // ONLINE_JUDGE
    while(scanf("%d%d",&n,&m)!=EOF){
        build();
        int x;
        while(m--){
            printf("==%d\n",res);
            scanf("%d",&x);
            solve(x);
        }
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值