Time Limit: 40 Sec
Memory Limit: 128 MB
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
题目分析
原本一开始的思路是树套树,结果写挂了,才猛然发现这数据范围不就tm莫队吗!!!
首先交换只相邻两项变成不下降序列的最少次数就是逆序对数
把莫队计数的数组改成树状数组
这样就可以边莫队边更新当前逆序对数了
注意离散化就好
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
#define lowbit(x) ((x)&(-x))
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=50010;
int n,m;
lt a[maxn],b[maxn],ans[maxn];
lt pos[maxn],sum[maxn],L=1,R,t;
struct node{int ll,rr,num;}q[maxn];
bool cmp(node a,node b){return (a.ll/t==b.ll/t) ?a.rr<b.rr :(a.ll/t<b.ll/t);}
void add(int x,int v){ for(int i=x;i<=n;i+=lowbit(i))sum[i]+=v;}
lt qsum(int x){ lt res=0; for(int i=x;i>0;i-=lowbit(i))res+=sum[i]; return res;}
int main()
{
n=read();
for(int i=1;i<=n;++i)
a[i]=b[i]=read();
sort(b+1,b+1+n);
for(int i=1;i<=n;++i)
if(i==1||b[i]!=b[i-1])
pos[++pos[0]]=b[i];
for(int i=1;i<=n;++i)
a[i]=lower_bound(pos+1,pos+1+pos[0],a[i])-pos;
m=read();
for(int i=1;i<=m;++i)
q[i].ll=read(),q[i].rr=read(),q[i].num=i;
t=sqrt(n);lt res=0;
sort(q+1,q+1+m,cmp);
for(int i=1;i<=m;++i)
{
while(R<q[i].rr){ res+=qsum(n)-qsum(a[R+1]); add(a[++R],1);}
while(R>q[i].rr){ res-=qsum(n)-qsum(a[R]); add(a[R--],-1);}
while(L<q[i].ll){ res-=qsum(a[L]-1); add(a[L++],-1);}
while(L>q[i].ll){ res+=qsum(a[L-1]-1); add(a[--L],1);}
ans[q[i].num]=res;
}
for(int i=1;i<=m;++i)
printf("%lld\n",ans[i]);
return 0;
}