题意:给长度为n的数列,求初始逆序对和交换两个点之后的逆序对。
题解:初始逆序对直接树状数组,交换两个点之后的逆序对和这两个点之间的区间有关,用分块记录每个块中数大小的情况。当两个端点不在同一个块时,要查询前半段、中间整块和后半段的情况,当两个端点在同一个块时,直接查询u到v。(注意题目给的区间不一定是u<v)
AC代码:
#include<stdio.h>
#include<algorithm>
#include<math.h>
#define N 20200
using namespace std;
int a[N],b[N];
int c[N],C[205][N];
int top;
int lowbit(int i)
{
return i&(-i);
}
void add(int i)
{
while(i<=top)
{
c[i]++;
i+=lowbit(i);
}
}
void addd(int pos,int i,int k)
{
while(i<=top)
{
C[pos][i]+=k;
i+=lowbit(i);
}
}
int sum(int i)
{
int ans=0;
while(i)
{
ans+=c[i];
i-=lowbit(i);
}
return ans;
}
int Sum(int pos,int i)
{
int ans=0;
while(i)
{
ans+=C[pos][i];
i-=lowbit(i);
}
return ans;
}
int L[N],R[N];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
top=unique(b+1,b+n+1)-b;
int ans=0;
int len=(int)sqrt(n);
for(int i=1;i<=n;i++)
{
int pos=lower_bound(b+1,b+top,a[i])-b;
ans+=sum(top)-sum(pos);
add(pos);
addd(i/len,pos,1);
if(L[i/len]==0)L[i/len]=i;
R[i/len]=i;
}
printf("%d\n",ans);
int m;
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
if(u>v) swap(u,v);
int dau,xiaou,dav,xiaov;
dau=xiaou=dav=xiaov=0;
int pos1=lower_bound(b+1,b+top,a[u])-b;
int pos2=lower_bound(b+1,b+top,a[v])-b;
int b1=u/len,b2=v/len;
if(b1!=b2)
{
for(int j=u+1;j<=R[b1];j++)
{
if(a[u]>a[j])xiaou++;
if(a[u]<a[j])dau++;
if(a[v]>a[j])xiaov++;
if(a[v]<a[j])dav++;
}
for(int j=b1+1;j<=b2-1;j++)
{
dau+=Sum(j,top-1)-Sum(j,pos1);
xiaou+=Sum(j,pos1-1);
dav+=Sum(j,top-1)-Sum(j,pos2);
xiaov+=Sum(j,pos2-1);
}
for(int j=L[b2];j<v;j++)
{
if(a[u]>a[j])xiaou++;
if(a[u]<a[j])dau++;
if(a[v]>a[j])xiaov++;
if(a[v]<a[j])dav++;
}
}
else
{
for(int j=u+1;j<v;j++)
{
if(a[u]>a[j])xiaou++;
if(a[u]<a[j])dau++;
if(a[v]>a[j])xiaov++;
if(a[v]<a[j])dav++;
}
}
ans+=dau-xiaou+xiaov-dav;
if(a[u]>a[v])ans--;
else if(a[u]<a[v])ans++;
printf("%d\n",ans);
addd(b1,pos1,-1);
addd(b1,pos2,1);
addd(b2,pos2,-1);
addd(b2,pos1,1);
swap(a[u],a[v]);
}
}