好友JZYshuraK推荐的莫队题目。
Description
Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号
。为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问。Mato每天随机选一个区间[l,r
],他今天就看编号在此区间内的这些资料。Mato有一个习惯,他总是从文件大小从小到大看资料。他先把要看的
文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序。排序程序可以在1单位时间内交换2个相邻的
文件(因为加密需要,不能随机访问)。Mato想要使文件交换次数最小,你能告诉他每天需要交换多少次吗?
Input
第一行一个正整数n,表示Mato的资料份数。
第二行由空格隔开的n个正整数,第i个表示编号为i的资料的大小。
第三行一个正整数q,表示Mato会看几天资料。
之后q行每行两个正整数l、r,表示Mato这天看[l,r]区间的文件。
n,q <= 50000
Output
q行,每行一个正整数,表示Mato这天需要交换的次数。
Sample Input
4
1 4 2 3
2
1 2
2 4
1 4 2 3
2
1 2
2 4
Sample Output
0
2
//样例解释:第一天,Mato不需要交换
第二天,Mato可以把2号交换2次移到最后。
2
//样例解释:第一天,Mato不需要交换
第二天,Mato可以把2号交换2次移到最后。
此题就是动态求逆序对。用莫队的思想移动左右端点,之后考虑逆序对改变的个数。用树状数组再次求解。
还有就是注意离散化a[i],写题时忘了。
#include<cstdio> #include<algorithm> using namespace std; #define N 100001 #define mod 240 int n,Q; int a[N]; int c[N]; int dist[N]; struct Query { int id,x,y,daan,blob; }query[N]; bool cmp(const Query &a,const Query &b) { if(a.blob!=b.blob) return a.blob<b.blob; else return a.y<b.y; } bool cmp2(const Query &a,const Query &b){return a.id<b.id;} int lowbit(int x){return x&-x;} void add(int x,int delta) { while(x<=n) { c[x]+=delta; x+=lowbit(x); } } int find(int x) { int ans=0; while(x) { ans+=c[x]; x-=lowbit(x); } return ans; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); dist[i]=a[i]; } sort(dist+1,dist+n+1); for(int i=1;i<=n;i++) a[i]=lower_bound(dist+1,dist+n+1,a[i])-dist; scanf("%d",&Q); for(int i=1;i<=Q;i++) { scanf("%d%d",&query[i].x,&query[i].y); if(query[i].x%mod==0) query[i].blob=query[i].x/mod; else query[i].blob=query[i].x/mod+1; query[i].id=i; } sort(query+1,query+Q+1,cmp); int l=1,r=0; int ans=0; for(int i=1;i<=Q;i++) { while(l<query[i].x) add(a[l],-1),ans-=find(a[l]-1),l++; while(l>query[i].x) add(a[--l],1),ans+=find(a[l]-1); while(r<query[i].y) add(a[++r],1),ans+=r-l+1-find(a[r]); while(r>query[i].y) add(a[r],-1),ans-=r-l-find(a[r]),r--; query[i].daan=ans; } sort(query+1,query+Q+1,cmp2); for(int i=1;i<=Q;i++) printf("%d\n",query[i].daan); return 0; }