[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; }