两行代码vector求逆序数(冒泡)
冒泡排序是保证前面冒完的泡泡是有序的~,所以说,我们可以直接利用这一点,二分找到要插入的数值应该在哪个位置,那么原位置减去当前应该在的位置就是它的逆序数
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
int read()
{
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 500005;
int a[maxN];
vector<int>vt;
int main()
{
int n; n = read();
ll ans = 0;
for(int i = 0; i < n; ++ i)
{
a[i] = read();
int id = upper_bound(vt.begin(), vt.end(), a[i]) - vt.begin();
ans += ll(i - id), vt.insert(id + vt.begin(), a[i]);
}
printf("%lld\n", ans);
return 0;
}
归并排序解决逆序数
归并排序采用了分治法,将两个有序的区间合并。将原区间不断划分为小区间,再进行归并排序,最终得到一个完全有序的序列,也叫二路归并。
定义合并空间: r a n k [ ] rank[ \ ] rank[ ]
定义指针
i
,
j
i, j
i,j分别为两个有序子区间的起始位置。
如果
a
[
i
]
<
=
a
[
j
]
a[i]<=a[j]
a[i]<=a[j]那么将
a
[
i
]
a[i]
a[i]放入合并空间,指针
i
i
i后移;否则,将
a
[
j
]
a[j]
a[j]放入合并空间,指针
j
j
j后移
那么如何求逆序数呢?
举个栗子!
上图状态下,
m
i
d
=
3
mid = 3
mid=3
①
a
[
i
=
1
]
>
a
[
j
=
4
]
a[i = 1] > a[j = 4]
a[i=1]>a[j=4],
a
[
4
]
=
1
a[4]=1
a[4]=1的逆序数为
3
=
m
i
d
−
i
+
1
3=mid-i+1
3=mid−i+1;合并序列{1};指针
j
j
j后移
②
a
[
i
=
1
]
<
a
[
j
=
5
]
a[i = 1] < a[j = 5]
a[i=1]<a[j=5],合并序列{1,2};指针
i
i
i后移
③
a
[
i
=
2
]
>
a
[
j
=
5
]
a[i = 2] > a[j = 5]
a[i=2]>a[j=5],
a
[
5
]
=
3
a[5]=3
a[5]=3的逆序数为
2
=
m
i
d
−
i
+
1
2=mid-i+1
2=mid−i+1;
…剩下的类似
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
int read()
{
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 500005;
int n, a[maxN], rk[maxN];
ll ans;
void msort(int l, int r)
{
if(l == r) return ;
int mid = (l + r) >> 1;
msort(l, mid); msort(mid + 1, r);
int i = l, j = mid + 1, k = l;
while(i <= mid && j <= r)
{
if(a[i] <= a[j]) rk[k ++ ] = a[i ++ ];
else rk[k ++ ] = a[j ++ ], ans += ll(mid - i + 1);
}
while(i <= mid) rk[k ++ ] = a[i ++ ];
while(j <= r) rk[k ++ ] = a[j ++ ];
for(int p = l; p <= r; ++ p ) a[p] = rk[p];
}
int main()
{
n = read();
for(int i = 1; i <= n; ++ i ) a[i] = read();
msort(1, n);
printf("%lld\n", ans);
return 0;
}
树状数组求逆序数
树状数组的作用是什么呢?答:存的是每个对应数值出现的次数。所以对于某个数 a [ i ] a[i] a[i]来说,将这个数插入树状数组之后,那么我们查询数值 [ 1 , a [ i ] ] [1,a[i]] [1,a[i]]出现次数的前缀和,就可以得到数组 a [ 1 , i ] a[1,i] a[1,i]中小于等于 a [ i ] a[i] a[i]的数的个数 n u m num num。那么逆序数就是 i − n u m i-num i−num~
因为数据给的很大,但是 n n n不大,所以我们需要对原数组进行离散化。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -f; ch = getchar(); }
while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int maxN = 5e5 + 10;
#define lowbit(x) x & (-x)
struct node{
int data, id;
friend bool operator < (node n1, node n2) { if(n1.data == n2.data) return n1.id < n2.id; return n1.data < n2.data;}
}a[maxN];
int n, disc[maxN];
int tree[maxN];
void Insert(int i, int val) {
while(i <= n) tree[i] += val, i += lowbit(i);
}
int query(int i) {
int res = 0;
while(i > 0) res += tree[i], i -= lowbit(i);
return res;
}
int main() {
n = read();
for(int i = 1; i <= n; ++ i ) a[i].data = read(), a[i].id = i;
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; ++ i ) disc[a[i].id] = i;
ll ans = 0;
for(int i = 1; i <= n; ++ i ) {
Insert(disc[i], 1);
ans += i - query(disc[i]);
}
printf("%lld\n", ans);
return 0;
}
例题:P1908 逆序对
对于这个题来说,第一种用冒泡的方法 O ( n 2 ) O(n^2) O(n2),肯定是TTTTTTTTT啊~后面两种 O ( n l o g n ) O(nlogn) O(nlogn)可以解决