zoukankan      html  css  js  c++  java
  • [BZOJ3289]Mato的文件管理

    题目大意

    给你(n)个数,(m)个询问

    每次询问一段区间([l,r]),可以进行交换相邻两个元素的操作,使这段区间从小到大排序的最小操作次数

    (n,mleq 50000),元素值在(1000000)内,没有重复元素

    解题思路

    这题第一感觉,好像和逆序对有关?

    那么给出性质:

    使区间[l,r]按题意方式从小到大排序的最小操作次数等价于区间[l,r]的逆序对总数

    这是一个很强的结论了

    尝试给出证明:

    首先证明操作次数的下限是逆序对个数

    很显然,每次操作(交换相邻两数)能且至多消除一个逆序对

    因此下限就是逆序对个数

    然后我们证明每次操作都可以消除一个逆序对

    同样很显然,假设不存在可以消除的逆序对,这意味着这个序列是有序的!

    同时,如果序列不是有序的,那么必定存在一对相邻的逆序对可以消除

    于是我们(类似贪心的)取到了最优方案

    维护

    于是这题就变成求区间逆序对个数了

    那么考虑用莫队和权值树状数组求解

    所谓权值树状数组,就是维护对应权值出现次数
    第i位上的值代表i的出现次数
    那么k的前缀和的含义就是不大于k的数一共有几个
    

    莫队最核心的就是得到指针移动对答案的影响

    我们考虑在末尾增加一个元素的情况:

    那个元素贡献的逆序对个数就是前面比它大的元素个数

    看样子需要两个权值树状数组,一个维护前缀和,一个后缀和?

    其实不用,因为比A大的元素个数就是总数-不大于A的元素个数

    这个可以直接用权值树状数组维护前缀和

    复杂度分析,(Blo=sqrt{n})最优

    复杂度(O(nsqrt{n}logn))

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    
    const int Base=230;
    int Blo[70000];
    
    int n,q;
    
    namespace tarr{
        int T[100000];
        void clear(){memset(T,0,sizeof(T));}
        inline int lowbit(int k){return k&-k;}
        void add(int K,int v){
            while (K<=n){
                T[K]+=v;
                K+=lowbit(K);
            }
        }
        int query(int K){
            int ret=0;
            while (K){
                ret+=T[K];
                K-=lowbit(K);
            }
            return ret;
        }
    }    
    
    struct query{
        int x,y;
        int id;
    }Q[100000];
    bool cmp_q(query X,query Y){return Blo[X.x]<Blo[Y.x]||(Blo[X.x]==Blo[Y.x]&&X.y<Y.y);}
    
    struct val{
        int V,id;
    }V[100000];
    bool cmp_v(val A,val B){return A.V<B.V;}
    
    int U[100000],valcnt;
    int L,R,val;
    int ans[100000];
    
    int main(){
        tarr::clear();
        for (int i=0;i<=50000/Base;i++)
            for (int j=1;j<=Base;j++)
                Blo[i*Base+j]=i;
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",&V[i].V),V[i].id=i;
        std::sort(V+1,V+n+1,cmp_v);
        for (int i=1;i<=n;i++){
            if (V[i].V!=V[i-1].V) valcnt++;
            U[V[i].id]=valcnt;
        }
        scanf("%d",&q);
        for (int i=1;i<=q;i++) 
            scanf("%d%d",&Q[i].x,&Q[i].y),Q[i].id=i;
        std::sort(Q+1,Q+q+1,cmp_q);
        L=R=1;tarr::add(U[1],1);
        for (int i=1;i<=q;i++){
            while (R<Q[i].y){
                R++;
                val+=R-L-tarr::query(U[R]-1);
                tarr::add(U[R],1);
            }
            while (L>Q[i].x){
                L--;
                val+=tarr::query(U[L]-1);
                tarr::add(U[L],1);
            }
            while (R>Q[i].y){
                val-=R-L-tarr::query(U[R]-1);
                tarr::add(U[R],-1);
                R--;
            }
            while (L<Q[i].x){
                val-=tarr::query(U[L]-1);
                tarr::add(U[L],-1);
                L++;
            }
            ans[Q[i].id]=val;
        }
        for (int i=1;i<=q;i++) printf("%d
    ",ans[i]);
    }
    
  • 相关阅读:
    MiniUI表单验证实践
    MiniUI官方表单验证示例
    MiniUI表单验证总结
    Js-事件分发与DOM事件流
    Windows远程桌面连接的利器-mRemote
    Git 以分支的方式同时管理多个项目
    GIT 如何合并另一个远程Git仓库的文件到本地仓库里某个指定子文件夹并不丢失远程提交记录?
    如何导入另一个 Git库到现有的Git库并保留提交记录
    Total Commander如何设置自定义快捷键在当前目录打开ConEmu
    PHP ECSHOP中 诡异的问题:expects parameter 1 to be double
  • 原文地址:https://www.cnblogs.com/ytxytx/p/9820960.html
Copyright © 2011-2022 走看看