第一行一个正整数n,表示Mato的资料份数。
第二行由空格隔开的n个正整数,第i个表示编号为i的资料的大小。
第三行一个正整数q,表示Mato会看几天资料。
之后q行每行两个正整数l、r,表示Mato这天看[l,r]区间的文件。
q行,每行一个正整数,表示Mato这天需要交换的次数。
4 1 4 2 3 2 1 2 2 4
0 2
Hint
n,q <= 50000
样例解释:第一天,Mato不需要交换
第二天,Mato可以把2号交换2次移到最后。
分析:题目本质就是询问区间的逆序对个数,离线莫队+树状数组。
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#define N 50004
using namespace std;
int n,q,block,f[N],s[N],a[N];
long long ans,Ans[N];
struct thing
{
int l,r,num,bl;
bool friend operator < (thing a,thing b)
{
if(a.bl == b.bl) return a.r < b.r;
return a.bl < b.bl;
}
}ask[N];
void Insert(int k,int x)
{
while(k <= n)
{
f[k] += x;
k += k & (-k);
}
}
int Find(int k)
{
int tot = 0;
while(k)
{
tot += f[k];
k -= k & (-k);
}
return tot;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&a[i]);
s[i] = a[i];
}
sort(s+1,s+1+n);
for(int i = 1;i <= n;i++)
a[i] = lower_bound(s+1,s+1+n,a[i]) - s;
scanf("%d",&q);
for(int i = 1;i <= q;i++)
{
scanf("%d%d",&ask[i].l,&ask[i].r);
ask[i].num = i;
}
block = (int)sqrt(n) + 1;
for(int i = 1;i <= q;i++)
ask[i].bl = (ask[i].l-1)/block + 1;
sort(ask+1,ask+1+q);
int l = 1,r = 1;
Insert(a[1],1);
for(int i = 1;i <= q;i++)
{
for(;r > ask[i].r;r--)
{
ans -= r - l + 1 - Find(a[r]);
Insert(a[r],-1);
}
for(r++;r <= ask[i].r;r++)
{
ans += r - l - Find(a[r]);
Insert(a[r],1);
}
r = ask[i].r;
for(;l < ask[i].l;l++)
{
ans -= Find(a[l]-1);
Insert(a[l],-1);
}
for(l--;l >= ask[i].l;l--)
{
ans += Find(a[l]-1);
Insert(a[l],1);
}
l = ask[i].l;
Ans[ask[i].num] = ans;
}
for(int i = 1;i <= q;i++) printf("%lld\n",Ans[i]);
}