[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\n",ans[i]);
    return 0;
}

 

posted @ 2017-11-22 21:26  lher  阅读(158)  评论(0编辑  收藏  举报