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

    Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号。为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问。Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料。Mato有一个习惯,他总是从文件大小从小到大看资料。他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序。排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能随机访问)。Mato想要使文件交换次数最小,你能告诉他每天需要交换多少次吗?
    Input

    第一行一个正整数n,表示Mato的资料份数。
    第二行由空格隔开的n个正整数,第i个表示编号为i的资料的大小。
    第三行一个正整数q,表示Mato会看几天资料。
    之后q行每行两个正整数l、r,表示Mato这天看[l,r]区间的文件。

    Output

    q行,每行一个正整数,表示Mato这天需要交换的次数。

    Sample Input

    4
    1 4 2 3
    2
    1 2
    2 4
    

    Sample Output

    0
    2
    

    Hint

    n,q <= 50000
    样例解释:第一天,Mato不需要交换
    第二天,Mato可以把2号交换2次移到最后。

    莫队。

    考虑转移。

    如果我们在当前区间左边加入一个数,那么它的代价就是它在原区间内比他小的数有几个。因为我们可以看做,在加入这个数以前,区间内的数是有序的,我们要把它通过一些交换往右移到一个合适的位置。

    同理,在当前区间右边加入一个数,它的代价就是它在原区间内比他大的数有几个。

    在当前区间左边删除一个数,它的代价就是它在原区间删除它之后,比他小的数有几个。因为我们可以倒着思考,当这个数被加入区间时,区间内比他小的数有几个。现在它被删除了,交换次数肯定要还原回去。

    在当前区间右边删除一个数,它的代价就是它在原区间删除它之后,比他大的数有几个。

    如何维护当前新加进的数是第几大?

    平衡树或者权值树状数组(线段树)。

    尽量不要出现下面这样的式子:b+=a+a--。(像这种式子尽量分开写:b+=a;a--;b+=a;)运算符优先级之类的东西很难受。在区间右边操作的时候,求原区间内比他大的数有几个,是要把区间总长拿去减。注意:区间总长是r++之前(r--之后的区间长),也不是询问的区间长。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct xxx{
        int l,r,lblock,id;
    }q[50100];
    struct xxx2{
        int a,id;
    }data[50100];
    int cnt[50100],a[50100],n,ans[50100];
    int lowbit(int x){return x&(-x);}
    void add(int x,int k){for(int i=x;i<=n;i+=lowbit(i))cnt[i]+=k;}
    int query(int x){int ans=0;for(int i=x;i;i-=lowbit(i))ans+=cnt[i];return ans;}
    bool cmp(xxx a,xxx b){return a.lblock!=b.lblock?a.lblock<b.lblock:a.r<b.r;}
    bool cmp2(xxx2 a,xxx2 b){return a.a<b.a;}
    int main()
    {
        int Q;scanf("%d",&n);int T=sqrt((double)n);
        for(int i=1;i<=n;i++)scanf("%d",&data[i].a),data[i].id=i;
        sort(data+1,data+1+n,cmp2);int last=0;
        for(int i=1;i<=n;i++)if(data[i].a==data[i-1].a)a[data[i].id]=last;else a[data[i].id]=++last;
        scanf("%d",&Q);
        for(int i=1;i<=Q;i++)
        {
            scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;q[i].lblock=(q[i].l-1)/T+1;
        }
        sort(q+1,q+Q+1,cmp);
        int l=1,r=0,sum=0;
        for(int i=1;i<=Q;i++)
        {
            while(r<q[i].r){sum+=(r-l+1);sum-=query(a[++r]);add(a[r],1);}
            while(r>q[i].r){add(a[r],-1);sum+=query(a[r--]);sum-=(r-l+1);}
            while(l<q[i].l){add(a[l],-1);sum-=query(a[l++]-1);}
            while(l>q[i].l){sum+=query(a[--l]-1);add(a[l],1);}
            ans[q[i].id]=sum;
        }
        for(int i=1;i<=Q;i++)printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    mysql联合索引命中条件
    Shiro知识初探(更新中)
    Java中使用MongoTemplate进行分批处理数据
    Java中String时间范围比较
    使用ReentrantLock
    使用Condition
    python的坑--你知道吗?
    python基础--函数全解析(1)
    CSS基本语法及页面引用
    HTML学习汇总
  • 原文地址:https://www.cnblogs.com/lher/p/7881358.html
Copyright © 2011-2022 走看看