【JAVA】求数组前K个数中固定位置元素之和Pro20210524 (优先级队列 / IndexTree)

这是一个关于计算满意度分数之和的问题,当接收的满意度为Y的倍数时,需要调查X/Y位置的满意度。文章介绍了两种解决方案:优先级队列和IndexTree,分别详细阐述了每种方法的思路和操作流程,通过实例解释了如何在不同情况下处理满意度分数的增删和计算。
摘要由CSDN通过智能技术生成

【JAVA】求数组前K个数中固定位置元素之和Pro20210524

题目

用户用餐后会在外卖应用上对食物的满意度进行评价。(满意度分数始终大于0)
研究满意度的专家称,当把满意度分数按照从小到大的顺序排列时,某个(X/Y)位置的满意度非常重要。因此作为管理满意度的负责人丽雅,她想逐个接收分数后进行处理。
每当接收的满意度为Y的倍数时,会调查X/Y位置的满意度并进行记录。(如果X=3,Y=4的情况,满意度有4个时取第三个分数,有八个时取第六个分数。)
然而,有时候用户也会删除他们的满意度分数。这种情况,为了方便会把该满意度分数接收为负数,然后删除该分数。(确定删除只会发生在已存在的分数上。)然而删除后,数据个数重新变成Y的倍数时,也仍然需要调查满意度分数并进行记录。

让我们思考一下 X=3,Y=4,并且按照 7、5、3、1、-1、7、7、5、6、1、-7、1 的顺序接收12个满意度分数的情况。
在这里插入图片描述

首先按照7、5、3、1的顺序输入时,输入的数据个数满足Y的倍数,所以按升序排列时,会成为[图1],X/Y位置的满意度分数是5。
在这里插入图片描述

接着输入了-1时,会如上[图2]一样,前面的1会被删除(请注意,负数值本身不是输入值,而是要删除的值), 然后输入7,会成为[图3]。此时,数据个数重新会变成Y的倍数,X/Y位置的满意度值为7。
在这里插入图片描述

然后输入7、5、6、1时,数据会成为Y的倍数,会有8个值按从小到大排序,此时跟[图4]一样,这时X/Y位置的值为7。
在这里插入图片描述

最后输入-7、1时,跟上述的条件一样,会删除一个跟-7的绝对值相同的7,然后添加1后,数据个数又会变成Y的倍数,成为[图 5]。此时的X/Y位置的值为6,所以符合条件的位置上所在的满意度分数之和为25(5+7+7+6)。

求出丽雅记录的满意度之和。

[限制条件]
1.输入的满意度分数的个数N为介于4到300,000之间的整数。
2.满意度分数为介于-1,000,000,000 到 1,000,000,000 之间的整数,分数为整数时,表示用户输入的满意度,分数为负数时,表示用户删除的满意度。不会给出0。
3.删除时,存在多个相同的满意度绝对值时,只删除其中一个满意度。
4.不会删除不存在的满意度。
5.X和Y是介于1到N的整数,且必须为互质数。
6.X小于Y。

[输入]
首先给出测试用例数量T,接着给出T种测试用例。每个测试用例的第一行给出数据个数N,以及空格区分给出表示比率的X,Y。第二行空格区分给出N个数字。

[输出]
每个测试用例输出一行。各测试用例输出#x(x是测试用例的编号,从1开始),加一个空格,输出丽雅调查的满意度之和。

[输入输出 示例]
(输入)
3
12 3 4
7 5 3 1 -1 7 7 5 6 1 -7 1
6 2 3
3 -3 2 4 4 1
9 2 3
7 2 6 2 7 7 3 3 7
(输出)
#1 25
#2 4
#3 20

方法一:优先级队列

思路:
维护两个优先级队列,一个降序,一个升序;
确保在有效元素的个数P是Y的倍数时,前 (P / Y) * X 个在升序的PQ中,后 P - (P / Y) * X 个在降序的PQ中;
添加数字时,优先往降序的PQ中添加(即:当 P / Y < X 时,往降序的PQ中添加;当 P / Y >= X 时,往升序的PQ中添加);
出现要删除的数D时,确认这个数在哪个PQ中(与降序PQ的有效元素中的最大值M比较,当 D <= M时,D在降序PQ中,反之则在升序PQ中),优先从降序的PQ中删除(不是真删除,需要一个集合,记录某个元素删除的次数);
当效元素的个数P是Y的倍数时,从降序PQ中获取最大有效元素(若获取的最大值,被删除的次数大于0时,重新获取,并将该元素的删除次数减一)累加为SUM;
SUM即为所求。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Comparator;
import java.util.HashMap;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Solution {
	static HashMap<Integer, Integer> hm;

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		int T = Integer.parseInt(st.nextToken());

		for (int t = 1; t <= T; t++) {
			st = new StringTokenizer(br.readLine());
			int N = Integer.parseInt(st.nextToken());
			int X = Integer.parseInt(st.nextToken());
			int Y = Integer.parseInt(st.nextToken());

			PriorityQueue<Integer> pqL = new PriorityQueue<Integer>(new Comparator<Integer>() {
				@Override
				public int compare(Integer o1, Integer o2) {
					return o2 - o1;
				}
			});

			PriorityQueue<Integer> pqR = new PriorityQueue<Integer>();

			pqL.add(0);
			pqR.add(20_0000_0001);

			int pqlSize = 0, pqrSize = 0, value = 0, leftMax, rightMin;

			long result = 0L;
			hm = new HashMap<Integer, Integer>();

			st = new StringTokenizer(br.readLine());

			for (int i = 0; i < N; i++) {
				value = Integer.parseInt(st.nextToken());

				leftMax = getEffectiveValue(pqL);
				rightMin = getEffectiveValue(pqR);

				if (value > 0) {
					if ((pqlSize + pqrSize) % Y < X) {
						if (value <= leftMax) {
							pqL.add(value);

							pqL.add(leftMax);
							pqR.add(rightMin);
						} else {
							pqL.add(Math.min(value, rightMin));
							pqR.add(Math.max(value, rightMin));

							pqL.add(leftMax);
						}

						pqlSize++;
					} else {
						pqL.add(Math.min(value, leftMax));
						pqR.add(Math.max(value, leftMax));

						pqR.add(rightMin);

						pqrSize++;
					}
				} else {
					int count = 1;
					if (hm.get(-value) != null && hm.get(-value) > 0) {
						count = hm.get(-value) + 1;
					}
					hm.put(-value, count);

					if ((pqlSize + pqrSize - 1) % Y < X) {
						pqlSize--;

						if (-value > leftMax) {
							pqR.add(leftMax);
						} else {
							pqL.add(leftMax);
						}

						pqR.add(rightMin);
					} else {
						pqrSize--;

						if (-value <= leftMax) {
							pqL.add(rightMin);
						} else {
							pqR.add(rightMin);
						}

						pqL.add(leftMax);
					}

				}

				if ((pqlSize + pqrSize) > 0 && (pqlSize + pqrSize) % Y == 0) {
					leftMax = getEffectiveValue(pqL);

					result += (long) leftMax;
					pqL.add(leftMax);
				}
			}

			System.out.println("#" + t + " " + result);
		}

		br.close();
	}

	private static int getEffectiveValue(PriorityQueue<Integer> q) {
		int value = q.poll();

		while (hm.get(value) != null && hm.get(value) > 0) {
			hm.put(value, hm.get(value) - 1);
			value = q.poll();
		}

		return value;
	}

}

方法二:IndexTree

思路:
将分数中的正值(或将负值取绝对值)散列化,则元素取值范围为 (0, 300000);
使用IndexTree的叶节点和元素值对应,按原数组元素(散列化后的值)顺序读取并处理;
添加数字时,为该数字对应的叶节点加1(同时更新其父节点);
出现要删除的数D时,该数字对应的叶节点减1(同时更新其父节点);
当效元素的个数P是Y的倍数时,求出第(P / Y) * X个元素所在的叶节点对应的值(散列化后的值),再找到原数组中对应的值(散列化前的值),累加为SUM;
SUM即为所求。

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
	static int N, X, Y, IDX = 524288;
	static int[] DATA, COPY, TREE;

	public static void main(String[] args) throws IOException {
		System.setIn(new FileInputStream("D:\\SW\\TestCase\\sample_input_20210524.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		int T = Integer.parseInt(st.nextToken());
		for (int t = 1; t <= T; t++) {
			st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken());
			X = Integer.parseInt(st.nextToken());
			Y = Integer.parseInt(st.nextToken());

			DATA = new int[N];
			COPY = new int[N];

			TREE = new int[IDX * 2];

			st = new StringTokenizer(br.readLine());
			for (int i = 0; i < N; i++) {
				DATA[i] = Integer.parseInt(st.nextToken());
				COPY[i] = DATA[i];
			}

			Arrays.sort(COPY);

			long ans = 0;
			for (int i = 0; i < N; i++) {
				int tmp = Arrays.binarySearch(COPY, Math.abs(DATA[i]));

				if (DATA[i] < 0) {
					update(IDX + tmp, -1);
				} else {
					update(IDX + tmp, 1);
				}

				if (TREE[1] > 0 && TREE[1] % Y == 0) {
					ans += COPY[getTargetIndex(TREE[1] / Y * X) - IDX];
				}
			}

			System.out.println("#" + t + " " + ans);
		}
	}

	static void update(int i, int val) {
		while (i > 0) {
			TREE[i] += val;
			i = i >> 1;
		}
	}

	static int getTargetIndex(int count) {
		int index = 1;

		while (index < IDX) {
			index *= 2;
			if (TREE[index] < count) {
				count -= TREE[index];
				index++;
			}
		}

		return index;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值