逆序对题解出处(1266)
老实说,还没有归并排序快
先上代码,再解释
#include<cstdio>
#include<algorithm>
using namespace std;
inline void read(long long &x) {
x=0;
long long f=1;
char s=getchar();
while(s<'0'||s>'9') {
if(s=='-')f=-1;
s=getchar();
}
while(s>='0'&&s<='9') {
x=x*10+s-48;
s=getchar();
}
x*=f;
}
inline void pr(long long x) {
if(x<0)x=-x;
if(x>9)pr(x/10);
putchar(x%10+48);
}
struct node {
long long num,id;
} a[500005];
long long lsh[500005],c[500005],b[500005],n,k,ans;
inline int lowbit(int x) {
return x&-x;
}
inline void update(int x,int k) {
for(int i=x; i<=n; i+=lowbit(i))
c[i]+=k;
}
inline int sum(int x) {
int ans=0;
for(int i=x; i>0; i-=lowbit(i))
ans+=c[i];
return ans;
}
inline bool cmp(node a,node b) {
return a.num<b.num;
}
int main() {
read(n);
for(int i=1; i<=n; i++)
read(a[i].num),a[i].id=i;
sort(a+1,a+1+n,cmp);
int cnt=0;
for(int i=1; i<=n; i++) {
if(a[i].num!=a[i-1].num)
cnt++;
lsh[a[i].id]=cnt;
}
for(int i=1; i<=n; i++) {
update(lsh[i],1);
ans+=i-sum(lsh[i]);
}
pr(ans);
}
现在我们来分段解释(快读快输不解释)
先来看树状数组是个啥
大概就是这么个玩意儿,有什么用后面再解释
然后来看lowbit:
lowbit:
lowbit(i)的意思是将 i 转化成二进制数之后,只保留最低位的1及其后面的0,截断前面的内容,然后再转成十进制数,这个数也是树状数组中i号位的子叶个数。比如lowbit(22)的意思是将 22 转化成二进制数之后,得到10110,保留末位的1及其后的0,并截断前面的内容,得到10,转化为十进制数为2,即lowbit(22)=2,证明C[22]的子叶数为2个。
1、求lowbit方法一:
原数为x(十进制),先将原数转化成二进制之后的最后一位1替换成0,然后再用原数减去替换掉最后一位1后的数(十进制相减),答案就是lowbit(i)的结果;
lowbit(i)
{
return i - ( i & ( i – 1 ) );
}
说明:i的二进制可以看做A1B(A是最后一个1之前的部分,B是最后一个1之后的0)
i-1的二进制可以看做A0C(C是和B一样长的1)
i & (i - 1)的二进制就是A1B & A0C = A0B
i – (i & (i - 1))的二进制就是A1B – A0B = 0…010…0
2、求lowbit方法二:
原数为i(十进制),先将原数转化成二进制之后,在与原数相反数的二进制按位与,答案就是lowbit(i)的结果;
lowbit(i)
{
return i & -i;
}
例如:lowbit(22)=2
22的二进制原码011010,正数的补码等于它的原码011010
-22的二进制原码111010,负数的补码等于它的原码取反加1,为100110
011010 & 100110 = 000010 正数转换成原码后依然是000010
所以lowbit(22)=2
不懂自己百度
根据这个特点
void update(int k,int x)
{
for(int i = k; i <= n; i += lowbit(i))
C[i] += x;
}
结合图片,可以求出c数组的值。
然后来看如何求前缀和:
求前缀和B[]段代码 (PS:此区间为前缀和,也就是1~i)
int Sum(int k)
{
for(int i = k; i > 0; i -= lowbit(i) )
B[k] += C[i];
return B[k];
}
自己结合图像理解
逆序对其实就是求前面有几个比他大的数,然后把每一个的和累加起来就好了
代码分析:
int main() {
read(n);
for(int i=1; i<=n; i++)
read(a[i].num),a[i].id=i;
sort(a+1,a+1+n,cmp);
int cnt=0;
for(int i=1; i<=n; i++) {
if(a[i].num!=a[i-1].num)//考虑重复的情况,这里可以用STL+二分离散化
cnt++;
lsh[a[i].id]=cnt;//离散化可以使数据很大时将数组开小
}
for(int i=1; i<=n; i++) {
update(lsh[i],1);
ans+=i-sum(lsh[i]);
}
pr(ans);
}
在最后为什么要用ans+=i-sum(lsh[i])留给大家自己思考