题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5497
解题思路:
用树状数组维护一段区间L,区间长度为m,依次枚举该区间的终点ai,即将该点加入到区间L来,把ai-m从原来的L中删除;
删去ai+m,影响就是少了后面所有比它小的逆序对数,以及前面所有比它大的逆序对数
添加ai,同理,多了所有比它小的逆序对数,以及前面所有比它大的逆序对数
这里用到了两个树状数组,比较难理解。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 100005;
struct Tree
{
int n,c[maxn];
void init(int n)
{
this->n = n;
memset(c,0,sizeof(c));
}
int lowbit(int x)
{
return x & -x;
}
void add(int i,int d)
{
while(i <= n)
{
c[i] += d;
i += lowbit(i);
}
}
int sum(int x)
{
int ans = 0;
while(x)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
}L,R;
int n,m,a[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
L.init(n);
R.init(n);
long long tmp = 0,ans;
for(int i = m + 1; i <= n; i++)
{
tmp += i - m - 1 - R.sum(a[i]);
R.add(a[i],1);
}
ans = tmp;
for(int i = m + 1; i <= n; i++)
{
tmp -= R.sum(a[i]-1);
tmp -= L.sum(n) - L.sum(a[i]);
R.add(a[i],-1);
tmp += R.sum(a[i-m]-1);
tmp += L.sum(n) - L.sum(a[i-m]);
L.add(a[i-m],1);
ans = min(ans,tmp);
}
printf("%lld\n",ans);
}
return 0;
}