Mato同学从各路神犇以各种方式(你们懂的)收集了许多资料,这些资料一共有n份,每份有一个大小和一个编号。为了防止他人偷拷,这些资料都是加密过的,只能用Mato自己写的程序才能访问。Mato每天随机选一个区间[l,r],他今天就看编号在此区间内的这些资料。Mato有一个习惯,他总是从文件大小从小到大看资料。他先把要看的文件按编号顺序依次拷贝出来,再用他写的排序程序给文件大小排序。排序程序可以在1单位时间内交换2个相邻的文件(因为加密需要,不能随机访问)。Mato想要使文件交换次数最小,你能告诉他每天需要交换多少次吗?
这道题第一眼看上去是一脸懵逼,仔细想一想后,如果我们知道[l,r]的答案,那其实我们就可以知道[l,r-1],[l,r+1],[l+1,r],[l-1,r]的答案。于是乎,便想到了莫队。转化过程略过,自己推。
但求一个区间<=x的个数(用log的时间),这个问题呢一开始没想到,之后才想到可以用树状数组,那么这道题就解决了。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
int l,r,id,s;
}q[51000],b[51000];
int n,m;
int a[51000],belong[51000],bl[51000],br[51000],t[51000];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int w)
{
for(int i=x;i<=n;i+=lowbit(i))t[i]+=w;
}
int getsum(int x)
{
int sum=0;
for(int i=x;i>=1;i-=lowbit(i))sum+=t[i];
return sum;
}
void fk()
{
int cnt=sqrt(n);
for(int i=1;i<=n;i++)
{
int bg=(i-1)/cnt+1;
belong[i]=bg;
if(bl[bg]==0)bl[bg]=i,br[bg-1]=i-1;
}
br[belong[n]]=n;
}
bool cmp1(node a,node b)
{
if(belong[a.l]==belong[b.l])
{
if(a.r>b.r)return false;
else return true;
}
if(belong[a.l]>belong[b.l])return false;
else return true;
}
bool cmp2(node a,node b)
{
if(a.id>b.id)return false;
else return true;
}
bool cmp3(node a,node b)
{
if(a.s>b.s)return false;
else return true;
}
int ans;
void solve()
{
int l=1,r=0;ans=0;
for(int i=1;i<=m;i++)
{
for(int j=r;j>=q[i].r+1;j--)
{
add(a[j],-1);ans-=j-l-getsum(a[j]);
}
for(int j=r+1;j<=q[i].r;j++)
{
add(a[j],1);ans+=j-l+1-getsum(a[j]);
}
for(int j=l;j<=q[i].l-1;j++)
{
add(a[j],-1);ans-=getsum(a[j]-1);
}
for(int j=l-1;j>=q[i].l;j--)
{
add(a[j],1);ans+=getsum(a[j]-1);
}
l=q[i].l,r=q[i].r;
q[i].s=ans;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i].s);b[i].id=i;
}
sort(b+1,b+n+1,cmp3);
for(int i=1;i<=n;i++)a[b[i].id]=i;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;
}
fk();
sort(q+1,q+m+1,cmp1);
solve();
sort(q+1,q+m+1,cmp2);
for(int i=1;i<=m;i++)printf("%d\n",q[i].s);
return 0;
}