暴力
//
// Created by Genes on 2020/10/8.
//
// 小朋友排队
// 求出 a[i]的逆序对
// 暴力做法 ,直接两重循环或者 使用 归并排序
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
int n;
int a[N];
ll sum[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
int res = 0;
for (int j = 0; j < i; j++) {
if (a[i] < a[j]) {
res++;
}
}
for (int j = i + 1; j < n; j++) {
if (a[i] > a[j]) {
res++;
}
}
sum[i] = res;
}
ll ans = 0;
for (int i = 0; i < n; i++) {
int x = sum[i];
ans += (x + 1) * x / 2;
}
cout << ans << endl;
return 0;
}
归并排序
//
// Created by Genes on 2020/10/8.
//
// 小朋友排队
// 归并排序
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
int n;
struct {
int h; //身高
int idx;//上一次位置
ll m; //移动长度
} q[N], tmp[N];
void merge_sort(int l, int r) {
if (l >= r) {
return;
}
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r);
int k = l, i = l, j = mid + 1;
while (i <= mid && j <= r) {
if (q[i].h <= q[j].h) {
tmp[k++] = q[i++];
} else {
tmp[k++] = q[j++];
}
}
while (i <= mid) {
tmp[k++] = q[i++];
}
while (j <= r) {
tmp[k++] = q[j++];
}
//更新上一位置 idx
//将本次移动长度累加到总长度
for (i = l; i < k; i++) {
q[i] = {tmp[i].h, i, tmp[i].m + abs(tmp[i].idx - i)};
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> q[i].h;
q[i].idx = i;
}
merge_sort(1, n);
ll cnt = 0;
for (int i = 1; i <= n; i++) {
cnt += (q[i].m + 1) * q[i].m / 2;
}
cout << cnt << endl;
return 0;
}
树状数组
//
// Created by Genes on 2020/10/8.
//
// 小朋友排队
// 树状数组做法
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
int n;
int c[N], a[N];
int sum[N];//sum数组存储每个小朋友的不高兴度
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
for (; x < N; x += lowbit(x)) {
c[x] += v;
}
}
int ask(int x) {
int ans = 0;
for (; x; x -= lowbit(x)) {
ans += c[x];
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
a[i]++;//身高是从0开始,所以++ 从1开始
}
// 求每个数前面有多少个数比它大
for (int i = 0; i < n; i++) {
sum[i] = ask(N - 1) - ask(a[i]);
add(a[i], 1);
}
//清空树状数组
memset(c, 0, sizeof c);
//找出比这个数小的数有多少个
//注意这里必须倒着更新,否则无法算出高的层的数值
for (int i = n - 1; i >= 0; i--) {
sum[i] += ask(a[i] - 1);
add(a[i], 1);
}
ll res = 0;
for (int i = 0; i < n; i++) {
//等差数列求和 不高兴度的总和为从1+2+..+sum[i]
res += (ll) sum[i] * (sum[i] + 1) / 2;
}
cout << res << endl;
return 0;
}
我宣布这是我最喜欢的归并代码,太妙了
归并排序太妙了,从整个过程中位置的变化考虑,而不是盯着每一次细节交换
确实,这一步太秒了!
q[i] = {tmp[i].h, i, tmp[i].m + abs(tmp[i].idx - i)};
这里的tmp[i].m为啥不是0呀?一开始不应该都没移动嘛?
太妙了,不考虑中间位置变化
归并排序的适用面更广
归并排序的最后 for (i = l; i < k; i++) {
q[i] = {tmp[i].h, i, tmp[i].m + abs(tmp[i].idx - i)};
}这点不太理解是什么意思o(╥﹏╥)o 为啥i从l到k q,tmp下标都是i
为啥 k要等于l
秒啊啊啊啊啊
不懂,为什么暴力能过,数据太水了吗?
为什么归并排序中不能是判断q[i] < q[j]呢?
相等不需要换
3 利用树状数组求出比三大的数的个数
3 2 同理
3 2 1 同理
是我开始想复杂了
感谢
归并的思想太妙了
%%%
tql
能不能在 if(q[i] > q[j]) 的时候用一个数组记录一下当前点的逆序对的数量?sum[cnt++] = mid - i + 1 ?
为什么要从1开始编号?
因为树状数组是从1开始的,而这里用树状数组来存身高,所以都加1,避免身高为0的情况,而都加1,身高的相对关系没有变,不影响结果
tql
大佬你好,我看了你的树形数组的写法觉得非常精妙,但是我有个地方不太理解,请问你注释里说的“/身高是从0开始,所以++ 从1开始”是什么意思?
就是给所有的编号偏移一位 从1开始编号
噢噢懂了,谢谢大佬!