一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第15天,点击查看活动详情。
题目描述
这是codeforces的线段树专题。
A - Segment Tree, part 1 - A. Inversions
Given a permutation pi of n elements, find for each ii the number of j such that j<i and pi<pj.
Input
The first line contains the number n (1≤n≤10^5), the second line contains n numbers pi. It is guaranteed that pi form a permutation of numbers from 1 to n.
Output
Print n numbers, the ii-th number is equal to the number of j such that j<i and pi<pj.
Example
input
5
4 1 3 5 2
output
0 1 1 0 3
问题解析
经典题。翻转一下就是求逆序对了。
这题有个先决条件:元素都是1~n的。准备一个全为0的线段树,然后我们从左往右遍历数组,每遍历到一个元素a[i],就计算一下线段树中1到a[i]-1这个区间的和。和为多少就说明有多少比他小的数出现在它左边。然后我们把线段树中a[i]的位置从0变成1。
为什么区间的和为多少就说明有多少比他小的数出现在它左边?每走完一个元素就把它在线段树中对应的值从0变成1,然后我们计算的是1~ a[i]-1 这个区间的区间和,1~a[i]-1的数肯定都是小于a[i]的。所以这个区间的区间和就说明有多少比他小的数先出现,而我们是从左往右遍历数组的,所以和有多少就说明有多少比他小的数出现在它左边。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 100050;
int f[4 * N], a[N];
void revice(int k, int l, int r, int x)
{
if (l == r)
{
f[k] = 1;
return;
}
int m = (l + r) / 2;
if (x <= m)revice(k + k, l, m, x);
else revice(k + k + 1, m + 1, r, x);
f[k] = f[k + k] + f[k + k + 1];
}
int calc(int k, int l, int r, int x, int y)
{
if (x == l && y == r)return f[k];
int m = (l + r) / 2;
if (y <= m)return calc(k + k, l, m, x, y);
else
if (x > m)return calc(k + k + 1, m + 1, r, x, y);
else return calc(k + k, l, m, x, m) + calc(k + k + 1, m + 1, r, m + 1, y);
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m;
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
vector<int>v(n);
for (int i = 1; i <= n; i++)
{
v[i - 1] = calc(1, 1, n, a[i], n);
revice(1, 1, n, a[i]);
}
for (auto i : v)cout << i << " ";
return 0;
}