• 题解 CF785E 【Anton and Permutation】


    题解 CF785E 【Anton and Permutation】

    ​一.闲谈

    听说本题分块可以过而且吊打树套树?orz。。。我果然还是太菜了。。。

    二.分析

    1.求逆序对

    简化题目:给出序列1-n,以及m个操作,每次交换两个数,求当前序列的逆序对数

    如果,交换的两个数相同,我们直接输出当前答案即可,那么其他的情况呢?

    假设,我们知道交换前的逆序对数,那么新序列的逆序对数便等于原逆序对数加上两个数交换后的贡献

    那么,我们来考虑如何计算两个数交换的贡献

    我们设两个数分别为x和y,我们再设find_max(l,r,x)表示区间l->r内比x大的数的个数,find_min(l,r,x)同理,再设a[x]表示x的位置

    那么交换后,与x,y有关的逆序对为(注:这里a[x]与a[y]尚未交换,且以下不考虑x与y形成的逆序对):

    find_max(1,a[y]-1,x)+find_min(a[y]+1,n,x)+find_max(1,a[x]-1,y)+find_min(a[x]+1,n,y)

    那么我们加上这个答案就好了吗?

    不对!思考下,交换前,可能有与x,y有关的逆序对,如果我们直接加上当前的逆序对的话,我们就可能重复计算了!所以,我们还需要把答案减去交换前的与x,y有关的逆序对数,即:

    find_max(1,a[x]-1,x)+find_min(a[x]+1,n,x)+find_max(1,a[y]-1,y)+find_min(a[y]+1,n,y)

    然后,我们再来考虑x,y形成的逆序对:

    交换后,x与y有两个情况:

    1.x与y形成一个逆序对:

    我们直接将答案加一即可

    2.x与y不形成逆序对,原逆序对消失:

    那么,我们是否需要将答案减一呢?

    不是的,因为,我们一开始,不是减去了与x,y有关的逆序对数嘛?这时,我们在x里减去了逆序对(x,y),又在y里减去了逆序对(x,y),而我们本来只需要减一次的却减了两次!所以我们也需要将答案加一!

    所以,两种情况的处理方式是相同的!

    2.求函数find_min、find_max

    现在,我们已经知道如何求逆序对了,但是,我们还需要知道如何求find_min、find_max两个函数,我们首先考虑优秀的权值线段树,然而,全局线段树并不擅长区间,于是我们启用它的升级版:主席树,然后我们就可以轻松地求出来了,不过,我们这时,发现,我们还有交换操作没用,如果,我们写普通的主席树,我们就需要对a[x]>a[y]的每一个主席树都修改一遍,这时,我们再给它升级一下:我们用带修主席树来解决这些问题!

    至于交换操作,我们可以如此理解:

    将a[x]位置上的x修改成y,将a[y]位置上的y修改成x,之后我们再把a[x]与a[y]交换一下就可以了!

    复杂度:O(mloglogn*巨大常数(雾))

    三.优化

    这题甚为毒瘤的一点在于:卡空间

    经过试验(类似方法),开long long的直接凉

    开int 2e7会MLE,开1e7会Re

    于是我愤怒之下,写了一个垃圾回收,然后空间开成1e7+5e6才过的QwQ

    注意:写垃圾回收时,不要把root[i]回收了,因为我们还需要查询值之类的操作。。。

    四.代码

    //#pragma GCC optimize()//手动Ox优化
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e7+5e6;
    struct node{
        int val,lson,rson;
    }t[N];
    int n,m;
    int a[N];
    int siz,root[N];
    int Now[N],Pas[N],L,R;
    int sta[N],top;
    inline int lowbit(int x){
        return x&-x;
    }
    inline int news(){
        if(top){
            return sta[top--];
        }
        return ++siz;
    }
    inline void delt(int x){
        sta[++top]=x;
    }
    //垃圾回收 
    inline void mest(int x){
        t[x].lson=t[x].rson=t[x].val=0;
    }
    inline void insert(int &now,int l,int r,int x){
        if(!now){
            now=news();
            mest(now);
        }
        t[now].val++;
        if(l==r){
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid){
            insert(t[now].lson,l,mid,x);
        }else{
            insert(t[now].rson,mid+1,r,x);
        }
    }
    inline int find_Max(int l,int r,int lc,int rc){
        int sum=0,tot=0;
        for(int i=1;i<=L;++i){
            sum-=t[Pas[i]].val;
            tot-=t[t[Pas[i]].rson].val;
        }
        for(int i=1;i<=R;++i){
            sum+=t[Now[i]].val;
            tot+=t[t[Now[i]].rson].val;
        }
        if(lc<=l&&r<=rc){
            return sum;
        }
        int mid=(l+r)>>1;
        if(rc<=mid){
            for(int i=1;i<=L;++i){
                Pas[i]=t[Pas[i]].lson;
            }
            for(int i=1;i<=R;++i){
                Now[i]=t[Now[i]].lson;
            }
            return find_Max(l,mid,lc,rc);
        }
        if(lc>mid){
            for(int i=1;i<=L;++i){
                Pas[i]=t[Pas[i]].rson;
            }
            for(int i=1;i<=R;++i){
                Now[i]=t[Now[i]].rson;
            }
            return find_Max(mid+1,r,lc,rc);
        }
        for(int i=1;i<=L;++i){
            Pas[i]=t[Pas[i]].lson;
        }
        for(int i=1;i<=R;++i){
            Now[i]=t[Now[i]].lson;
        }
        return find_Max(l,mid,lc,rc)+tot;
    }
    inline int find_Min(int l,int r,int lc,int rc){
        int sum=0,tot=0;
        for(int i=1;i<=L;++i){
            sum-=t[Pas[i]].val;
            tot-=t[t[Pas[i]].lson].val;
        }
        for(int i=1;i<=R;++i){
            sum+=t[Now[i]].val;
            tot+=t[t[Now[i]].lson].val;
        }
        if(lc<=l&&r<=rc){
            return sum;
        }
        int mid=(l+r)>>1;
        if(rc<=mid){
            for(int i=1;i<=L;++i){
                Pas[i]=t[Pas[i]].lson;
            }
            for(int i=1;i<=R;++i){
                Now[i]=t[Now[i]].lson;
            }
            return find_Min(l,mid,lc,rc);
        }
        if(lc>mid){
            for(int i=1;i<=L;++i){
                Pas[i]=t[Pas[i]].rson;
            }
            for(int i=1;i<=R;++i){
                Now[i]=t[Now[i]].rson;
            }
            return find_Min(mid+1,r,lc,rc);
        }
        for(int i=1;i<=L;++i){
            Pas[i]=t[Pas[i]].rson;
        }
        for(int i=1;i<=R;++i){
            Now[i]=t[Now[i]].rson;
        }
        return find_Min(mid+1,r,lc,rc)+tot;
    }
    inline int find_min(int l,int r,int x){
        if(!r){
            return 0;
        } 
        if(x==1){
            return 0;
        } 
        int now=l-1;
        L=R=0;
        while(now){
            Pas[++L]=root[now];
            now-=lowbit(now);
        }
        now=r;
        while(now){
            Now[++R]=root[now];
            now-=lowbit(now);
        }
        return find_Min(1,n,1,x-1);
    }
    inline int find_max(int l,int r,int x){//寻找l-r区间内,比x大的数
        if(l==n+1){
            return 0;
        }
        if(x==n){
            return 0;
        }
        int now=l-1;
        L=R=0;
        while(now){
            Pas[++L]=root[now];
            now-=lowbit(now);
        }
        now=r;
        while(now){
            Now[++R]=root[now];
            now-=lowbit(now);
        }
        return find_Max(1,n,x+1,n);
    }
    inline void delted(int now,int l,int r,int x){
        t[now].val--;
        if(l==r){
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid){
            delted(t[now].lson,l,mid,x);
            if(!t[t[now].lson].val){
                delt(t[now].lson);
                t[now].lson=0;
            }
            return;
        }else{
            delted(t[now].rson,mid+1,r,x);
            if(!t[t[now].rson].val){
                delt(t[now].rson);
                t[now].rson=0;
            }
        }
    }
    inline void change(int t,int x,int y){//将t位置的x修改为y 
        int now=t;
        while(now<=n){
            delted(root[now],1,n,x);
            insert(root[now],1,n,y);
            now+=lowbit(now);
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i){
            a[i]=i;
            int now=i;
            while(now<=n){
                insert(root[now],1,n,i);
                now+=lowbit(now);
            }
        }
        long long ans=0;
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(x>y){
                swap(x,y);
            }
            if(x==y){
                printf("%lld
    ",ans);
                continue;
            }
            ans-=(find_max(1,a[x]-1,x)+find_min(a[x]+1,n,x));
            ans-=(find_max(1,a[y]-1,y)+find_min(a[y]+1,n,y));
            ans+=(find_max(1,a[y]-1,x)+find_min(a[y]+1,n,x));
            ans+=(find_max(1,a[x]-1,y)+find_min(a[x]+1,n,y));
            ans++;
            change(a[x],x,y),change(a[y],y,x);
            swap(a[x],a[y]);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    提前期分类
    物料属性,MRP/MPS属性
    ASP.NET刷新页面的一些方法
    Nothing 和 Is
    三层架构与MVC
    ADO.NET
    软件工程之数据流程图(DFD Data Flow Diagram)
    VB.NET小结
    推荐开发人员看的具有影响力的书籍
    C++考试
  • 原文地址:https://www.cnblogs.com/ThinkofBlank/p/10412478.html
Copyright © 2020-2023  润新知