• 【XSY1952】【BZOJ3295】动态逆序对(树套树)


    (Description)

    对于序列(A),它的逆序对数定义为满足(i<j),且(A_{i})>(A_{j})的数对((i,j))的个数。给(1)(n)的一个排列,按照某种顺序依次删除(m)个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。


    (Input)

    输入第一行包含两个整数(n)(m),即初始元素的个数和删除的元素个数。以下(n)行每行包含一个(1)(n)之间的正整数,即初始排列。以下(m)行每行一个正整数,依次为每次删除的元素。


    (Output)

    输出包含(m)行,依次为删除每个元素之前,逆序对的个数。


    (Sample Input)

    5 4
    1
    5
    3
    4
    2
    5
    1
    4
    2


    (Sample Output)

    5
    2
    2
    1


    (HINT)

    样例解释
    ((1,5,3,4,2)⇒(1,3,4,2)⇒(3,4,2)⇒(3,2)⇒(3)。)

    (M≤N≤100000)


    思路

    我们第一眼看上去,哇,三维偏序

    再看一眼,哇,待修三维偏序

    于是我们选择用树套树实现

    在这里我们选择树状数组套权值线段树

    但是我们发现,如果直接开点可能会爆空间?!

    那就动态开点线段树咯

    我们在树状数组每个节点开一棵线段树,维护每个区间权值不同的数的个数

    而树状数组用前缀和维护权值大于的关系

    最后每删去一个数就减去这个数对逆序对的贡献


    这里特殊把(getans)拎出来讲讲

    long long getans(int x,int p)
    {
        return query(p,n)-query(p,x)+query(n,x)-query(p,x);
    }
    

    这是在删除一个数时维护对逆序对的贡献的代码

    (query(x,y))是查询下标小于(x)的数中值小于(y)的数的个数

    于是,上面这段代码我们可以分为两段理解

    (1.query(p,n)-query(p,x))表示在小于(p)的下标中,值在(x)(n)中的数的个数
    (2.query(n,x)-query(p,x))表示在(p)(n)的下标中,值小于(x)的数的个数

    显而易见,这两个加起来就是一个数对逆序对的贡献,减去即可


    完整代码:

    #include<bits/stdc++.h>
    #define lowbit(x) (x&(-x))
    using namespace std;
    const int N=100010;
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')f=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<3)+(x<<1)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    int n,m;
    int pos[N];
    int c[N];
    int rt[N];
    int cnt=0;
    struct tree
    {
        int sum,ch[2];
    }t[N*100];
    void add(int x,int y)
    {
        for(;x<=n;x+=lowbit(x))c[x]+=y;
    }
    void modify(int &k,int l,int r,int x,int val)
    {
        if(!k)k=++cnt;
        t[k].sum+=val;
        //动态开点
        if(l==r)return ;
        int mid=(l+r)>>1;
        if(x<=mid)modify(t[k].ch[0],l,mid,x,val);
        else modify(t[k].ch[1],mid+1,r,x,val);
    }
    void ins(int x,int p,int v)
    {
        for(;x<=n;x+=lowbit(x))modify(rt[x],1,n,p,v);
    }
    long long getnum(int x)
    {
        long long ans=0;
        for(;x;x-=lowbit(x))ans+=c[x];
        return ans;
    }
    long long ask(int k,int l,int r,int p)//查询在区间内小于p的数
    {
        if(l==r)return t[k].sum;
        int mid=(l+r)>>1;
        if(p<=mid)return ask(t[k].ch[0],l,mid,p);
        return t[t[k].ch[0]].sum+ask(t[k].ch[1],mid+1,r,p);
    }
    long long query(int x,int p)
    {
        long long ans=0;
        for(;x;x-=lowbit(x))ans+=ask(rt[x],1,n,p);
        return ans;
    }
    long long getans(int x,int p)
    {
        return query(p,n)-query(p,x)+query(n,x)-query(p,x);
    }
    int main()
    {
        n=read(),m=read();
        int x;
        long long ans=0ll;
        for(int i=1;i<=n;i++)
        {
            x=read();
            pos[x]=i;
            ans+=getnum(n)-getnum(x);//在没有修改之前,直接用树状数组前缀和查询大于x的数个数,满足逆序对
            add(x,1);//将x的树状数组+1
            ins(i,x,1);//加入线段树中
        }
        for(int i=1;i<=m;i++)
        {
            printf("%lld
    ",ans);
            x=read();
            ans-=getans(x,pos[x]);//减去这个点对逆序对的贡献
            ins(pos[x],x,-1);//在线段树中-1
        }
        return 0;
    }
    
    
  • 相关阅读:
    glog入门demo
    gflag的简单入门demo
    caffe库源码剖析——net层
    排序算法的c++实现——计数排序
    docker的/var/lib/docker目录迁移
    SpringCloud Ribbon 负载均衡 通过服务器名无法连接的神坑一个
    Spring Boot Cache使用与整合
    Navicat Keygen
    Windows / Office
    docker swarm 搭建与服务更新
  • 原文地址:https://www.cnblogs.com/ShuraEye/p/11396863.html
Copyright © 2020-2023  润新知