1.题目描述:点击打开链接
2.解题思路:本题要求找出经过若干次交换后的数组中逆序对的个数。可以利用树状数组(BIT)解决。计数时可以分为两部分来分别统计。第一部分是统计位置i右边的所有不在原位置的元素中,小于rk[i]的个数。注意:这一部分统计的都是位置发生过变动的元素。第二部分统计位置i右边中仍在原位置的元素中,小于rk[i]的个数。接下来我们考虑如何快速求解这两部分的个数。
第一部分:因为这部分统计的元素均是不在原位置的元素。因此可以考虑用BIT求解。这是BIT的一个经典运用,务必要理解这种方法的原理:假设一共有cnt个不同的位置,从后往前依次考虑每个位置rk[i],如果设x[rk[i]]表示是否存在rk[i]这个元素,存在则设为1,否则设为0,那么所有小于rk[i]的元素就是x[rk[i]-1]+x[rk[i]-2]+...+x[2]+x[1]。而这正是BIT的一个经典的用法——求前缀和!由于是从后往前扫描的,因此凡是被标记过的肯定是位于i右边的元素!这里引入x数组只是为了便于理解,实际上只有s数组用来统计每个结点的和。
第二部分:举例解释如何计算这一部分。我们考虑这样的一个数组:1,2,3,4,5,6 。执行操作2,6,那么数组变为:1,6,3,4,5,2。再执行操作1,4,那么数组变为4,6,3,1,5,2.这时,我们考察元素6的第二部分的逆序对数。可以发现,仍在原位置的只有3,5。因此对于元素6,第二部分逆序对数是2。这个数我们也可以这样求解得到:提取出发生了位置变化的元素,即4,6,1,2。对其排序得到:1,2,4,6。在这个数组中,可以利用递推式sum[i]=sum[i-1]+x[i]-x[i-1]-1求出区间(0,i)之间小于x[i]且仍在原位置的元素的个数,不难得到sum[4]=2(x[4]=6),sum[2]=0(x[2]=2),所以元素6的第二部分逆序个数为sum[4]-sum[2],即2-0=2个。
这样,只需要将两部分得到的逆序对数相加,即可得到离散化后下标i表示的位置rk[i]的逆序对数。累加所有的逆序对数即可得到答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
int a[200005];
int b[200005];
int c[200005];
int d[200005];
int lb(int x)
{
return x&(-x);
}
struct node
{
int pos,num;
}p[200005];
bool cmp(node x,node y)
{
return x.pos<y.pos;
}
void add(int x)
{
while(x<=200000)
{
c[x]+=1;
x+=lb(x);
}
}
int gt(int x)
{
int ans=0;
while(x>=1)
{
ans+=c[x];
x-=lb(x);
}
return ans;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(c,0,sizeof(c));
int cnt1=0;
int cnt2=0;
map<int,int>mp;
for(int i=0; i<n; i++)
{
int u,v;
scanf("%d%d",&u,&v);
a[cnt1++]=u;
a[cnt1++]=v;
// if(!(mp[u]==0&&mp[v]==0))
if(mp[u]==0)
mp[u]=u;
if(mp[v]==0)
mp[v]=v;
swap(mp[u],mp[v]);
}
map<int,int>::iterator it;
it=mp.begin();
while(it!=mp.end())
{
int pos=it->first;
int num=it->second;
p[cnt2].pos=pos;
p[cnt2].num=num;
it++;
cnt2++;
}
sort(a,a+cnt1);
cnt1=unique(a,a+cnt1)-a;
sort(p,p+cnt2,cmp);
// cout<<cnt1<<endl;
// for(int i=0;i<cnt1;i++)
// cout<<a[i]<<" ";
long long ans=0;
for(int i=0;i<cnt2;i++)
{
b[i]=upper_bound(a,a+cnt1,p[i].num)-a+1;
int les=gt(b[i]);
ans+=(i-les);
add(b[i]);
}
int cnt3=0;
for(int i=0;i<cnt2;i++)
{
d[i]=p[i].pos;
}
//cout<<ans<<endl;
for(int i=0;i<cnt2;i++)
{
int x=p[i].pos;
int y=p[i].num;
int id1=lower_bound(d,d+cnt2,x)-d;
int id2=lower_bound(d,d+cnt2,y)-d;
if(x!=y)
{
ans+=(abs(y-x)-abs(id1-id2));
//ans++;
}
}
printf("%lld\n",ans);
}
}