⭐ 原题链接
⭐ 贪心:先入为主,猜结论再证明
⭐ 冒泡排序
有 n 个逆序对,至少交换 n 次(因为交换一次只减少一个逆序对)
⭐ 交换次数
每个小朋友的交换次数 = 前面比他高的数 + 后面比他矮的数
树状数组
🤠 树状数组记录每个 高度 出现多少次,然后分别从左往右、从右往左枚举小朋友,可以快速找到比当前小朋友 高或矮 的同学个数
🥚 query查询的是闭区间
前边比当前高 i - query(hi)
后边比当前矮 query(hi-1)
👨🏫 O(n logn)
👨🏫 数组要开到最大高度 10^6
import java.io.*;
import java.util.*;
public class Main
{
static int N = 1000010, n;
static int[] tr = new int[N];// 维护 i 出现 的次数
static int[] h = new int[N];
static int[] cnt = new int[N];// 记录第 i 个小朋友 前比他高后比他矮的同学个数
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
static int lowbit(int x)
{
return -x & x;
}
static void add(int x, int v)
{
for (int i = x; i <= n; i += lowbit(i))
tr[i] += v;
}
static int query(int x)
{
int res = 0;
for (int i = x; i != 0; i -= lowbit(i))
res += tr[i];
return res;
}
public static void main(String[] args) throws IOException
{
// 输入
n = Integer.parseInt(in.readLine());
String[] ss = in.readLine().split(" ");
for (int i = 1; i <= n; i++)
h[i] = Integer.parseInt(ss[i - 1]) + 1;// 使所有数非 0 ,方便操作
in.close();
// 求出前边 比 当前高的 个数
for (int i = 1; i <= n; i++)// 从前往后枚举
{
add(h[i], 1);// 给 h[i] 这个数 的个数 +1
// 当前小朋友总数 i - 比 h[i]小的个数 query[h[i]] = 比 h[i] 大的个数
cnt[i] = i - query(h[i]);
}
// 求出后边 比 当前矮的个数
Arrays.fill(tr, 0);
// 从后往前枚举
for (int i = n; i > 0; i--)
{
add(h[i], 1);
cnt[i] += query(h[i] - 1);
}
long res = 0;
for (int i = 1; i <= n; i++)
res += (long) (1 + cnt[i]) * cnt[i] / 2;
// System.out.println(res);
out.write(res + "");
out.flush();
}
}
归并排序
⭐ 分而治之(思维跳跃)(闲的没事才写这玩意😡)
import java.io.*;
public class Main
{
static Ch[] chs = new Ch[100010];
static Ch[] tmp = new Ch[100010];
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// 小朋友类
static class Ch
{
int h;// 小朋友的高度
int num;// 需要交换的次数
public Ch(int h)
{
this.h = h;
}
}
public static void main(String[] args) throws IOException
{
// 输入
int n = Integer.parseInt(in.readLine());
String[] ss = in.readLine().split(" ");
for (int i = 0; i < n; i++)
chs[i] = new Ch(Integer.parseInt(ss[i]));
in.close();
mergeSort(0, n - 1);
long res = 0;
for (int i = 0; i < n; i++)
res += cal(chs[i].num);
System.out.println(res);
}
private static long cal(int num)
{
return (long) (num + 1) * num / 2;
}
// 分
private static void mergeSort(int l, int r)
{
if (l == r)
return;
int mid = l + r >> 1;
mergeSort(l, mid);
mergeSort(mid + 1, r);
merge(l, mid, r);
}
// 并
private static void merge(int l, int mid, int r)
{
int i = l;//左区间起点
int j = mid + 1;//右区间起点
int k = l;//临时数组起点
while (i <= mid && j <= r)
{
if (chs[i].h <= chs[j].h)
{
// 处理后边比它小的
chs[i].num += j - mid - 1;// 相对于i来说,j 前面的数都比它小
tmp[k++] = chs[i++];
} else
{
// 处理前边比它大的
chs[j].num += mid - i + 1; // 相对于j来说,i 后面的数都比它大
tmp[k++] = chs[j++];
}
}
// 处理最后某个区间剩余的数
while (i <= mid)
{
// 此时右边区间的所有的都是小于i所在位置的小朋友的
chs[i].num += r - mid;
tmp[k++] = chs[i++];
}
while (j <= r)
tmp[k++] = chs[j++];
// 更新区间
for (int m = l; m <= r; m++)
{
chs[m] = tmp[m];
}
}
}