BZOJ3289 Mato的文件管理
好友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;
}

浙公网安备 33010602011771号