贪心构造c数组,然后求逆序数就完了
#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
const int maxn=5e5+5;
int n;
LL a[maxn],b[maxn];//原数组和排序后的数组
LL pos[maxn];
LL ms(int l,int r)// 求数组a的逆序数的板子
{
if(l >= r) return 0;
int m = (l + r) >> 1;// 分治
LL t1 = ms(l, m), t2 = ms(m + 1, r);//获得前半部分 和 后半部分 的逆序对个数
LL t3 = 0;//两边归并的逆序对个数
int i = l, j = m + 1, p = l;//前半部分起点、终点和当前排序的编号
while(i <= m && j <= r)//进行排序操作
{
if(a[i] > a[j])//如果前部分的某个元素大于后半部分某个元素
{
b[p++] = a[j++];//排序
t3 += m - i + 1;//因为前m元素中:i后的元素都比a[i]大,所以也比a[j]大,所以逆序对有m-i+1(包括自己)个
}
else b[p++] = a[i++];//排序
}
//完成剩下的归并操作
while(i <= m) b[p++] = a[i++];
while(j <= r) b[p++] = a[j++];
for(int i = l; i <= r; i++) a[i] = b[i];//将排序的值返回给a
return t1 + t2 + t3;//返回
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld", &a[i]);
if(pos[a[i] + 1]) ++a[i];
pos[a[i]] = i;
}
printf("%lld",ms(1,n));
return 0;
}
结论:把一个无序的序列变成有序的序列,每次操作可以交换相邻两个数,最少需要的操作次数是这个序列的逆序数
一个数
x
x
x 对逆序数的贡献最多是
x
−
1
x-1
x−1
求逆序数利用桶排序的思想,遍历
A
[
i
]
A[i]
A[i],我们每次先用
g
e
t
s
u
m
(
A
[
i
]
−
1
)
getsum(A[i]-1)
getsum(A[i]−1) 求出
[
1
,
A
[
i
]
−
1
]
[1,A[i]-1]
[1,A[i]−1] 有多少个数已经出现了,然后
(
A
[
i
]
−
1
)
−
g
e
t
s
u
m
(
A
[
i
]
−
1
)
(A[i]-1)-getsum(A[i]-1)
(A[i]−1)−getsum(A[i]−1) 就是有多少小于
A
[
i
]
A[i]
A[i]的数没有出现,那么必然在
A
[
i
]
A[i]
A[i]的后边,构成逆序对,加入答案。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 5;
ll n, m, sum[MAXN], A[MAXN];
struct node
{
int val, id;
bool operator<(const node &b)const{
return val == b.val ? id < b.id : val < b.val;
}
} a[MAXN], b[MAXN];
ll getsum(int i)
{
ll ans = 0;while(i > 0) ans += sum[i], i -= i & (-i);return ans;
}
void update(int i, ll k)
{
while(i <= n) sum[i] += k, i += i & (-i);
}
int main()
{
//ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i].val), a[i].id = i;
for (int i = 1; i <= n; ++i) scanf("%d", &b[i].val), b[i].id = i;
sort(a + 1, a + 1 + n);sort(b + 1, b + 1 + n);
for(int i = 1; i <= n; ++i) A[a[i].id] = b[i].id;
ll ans = 0;
for(int i = 1; i <= n; ++i)
ans += A[i] - 1 - getsum(A[i] - 1), update(A[i], 1ll), ans %= 99999997;
printf("%lld\n", ans);
return 0;
}
求逆序对加强版
题意:
给定序列求逆序对,有重复元素
2
s
2s
2s,开
O
2
O^2
O2
1
s
1s
1s
树状数组需要重新排序重复元素的影响,重新构造一个序列
为了消除相同元素的影响,只要所有与
a
i
a_i
ai 相等的元素中,先出现的标记也更小就好了(我们只统计相对更大的)。具体只需要在排序时将
a
i
a_i
ai 作为第一关键字,下标(第几个出现)作为第二关键字从小到大排序即可。
这样相同的元素中,后边于它相同的元素编号是比他大的,不会对答案产生贡献
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 5e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll c[maxn], Rank[maxn];
struct node{
ll x, id;
bool operator<(const node &B)const{
if(x != B.x) return x < B.x;
else return id < B.id;
}
}a[maxn];
int cnt;
int getsum(int i, ll ans = 0){
while(i) ans += c[i], i -= i & (-i);return ans;
}
void update(int i, ll k){
while(i <= n) c[i] += k, i += i & (-i);
}
void work()
{
cin >> n;
for(int i = 1; i <= n; ++i) cin >> a[i].x, a[i].id = i;
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; ++i) Rank[a[i].id] = i;
ll ans = 0;
//for(int i = 1; i <= n; ++i) cout << a[i].x << " ";cout << endl;
//for(int i = 1; i <= n; ++i) cout << Rank[i] << " ";cout << endl;
for(int i = 1; i <= n; ++i){
ans += i - 1 - getsum(Rank[i] - 1);
update(Rank[i], 1ll);
}
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}
归并排序code:
1
s
1s
1s 左右,开
O
2
O^2
O2
900
m
s
900ms
900ms
不需要重新构造序列,重复元素不影响求答案,速度较快,稳定
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 5e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll a[maxn], b[maxn];
ll ms(int l, int r)
{
if(l >= r) return 0ll;
int mid = l + r >> 1;
ll ans = ms(l, mid) + ms(mid + 1, r);
int i = l, j = mid + 1, k = l;
while(i <= mid && j <= r)
{
if(a[i] > a[j]){
ans += mid - i + 1;
b[k++] = a[j++];
}
else b[k++] = a[i++];
}
while(i <= mid) b[k++] = a[i++];
while(j <= r) b[k++] = a[j++];
for(int i = l; i <= r; i++) a[i] = b[i];//将排序的值返回给a
return ans;
}
void work()
{
cin >> n;
for(int i = 1; i <= n; ++i) cin >> a[i];
cout << ms(1, n);
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}