[BZOJ3289]Mato的文件管理

题目大意

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

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

\(n,m\leq 50000\),元素值在\(1000000\)内,没有重复元素

解题思路

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

那么给出性质:

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

这是一个很强的结论了

尝试给出证明:

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

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

因此下限就是逆序对个数

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

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

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

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

维护

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

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

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

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

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

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

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

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

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

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

复杂度\(O(n\sqrt{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\n",ans[i]);
}
posted @ 2018-10-20 11:30  ytxytx  阅读(122)  评论(0编辑  收藏  举报