蓝桥杯 小朋友排队 树状数组 归并

原题链接
在这里插入图片描述

⭐ 贪心:先入为主,猜结论再证明

⭐ 冒泡排序

有 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];
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值