OD统一考试
题解: Java / Python / C++
题目描述
某部门计划通过结队编程来进行项目开发,已知该部门有 N 名员工,每个员工有独一无二的职级,每三个员工形成一个小组进行结队编程,结队分组规则如下:
从部门中选出序号分别为 i、j、k 的3名员工,他们的职级分别为 level[i],level[j],level[k],结队小组满足 level[i] < level[j] < level[k] 或者 level[i] > level[j] > level[k],其中 0 ≤ i < j < k < n。
请你按上述条件计算可能组合的小组数量。同一员工可以参加多个小组。
输入描述
第一行输入:员工总数 n
第二行输入:按序号依次排列的员工的职级 level,中间用空格隔开
备注:
1 <= n <= 6000
1 <= level[i] <= 10^5
输出描述
可能结队的小组数量
示例1
输入:
4
1 2 3 4
输出:
4
说明:
可能结队成的组合(1,2,3)、(1,2,4)、(1,3,4)、(2,3,4)
示例2
输入:
3
5 4 7
输出:
0
说明:
根据结队条件,我们无法为该部门组建小组
题解
使用线段树 的解法,该解法通过统计每个员工左边比他小的员工数(
lt
数组),以及右边比他大的员工数(gt
数组),然后利用线段树来快速统计某个区间内的数目,最终计算可能的结队小组数量。具体步骤如下:
- 对每个员工,使用线段树统计他左边比他小的员工数(
lt
数组)和右边比他大的员工数(gt
数组)。- 遍历员工,对于每个员工,计算可能的结队小组数量,累加到结果中。
- 输出最终的结果。
这个解法的时间复杂度为O(N log N),其中N是员工的数量。这是因为在线段树的构建和查询过程中,每个员工都要进行O(log N)的操作。空间复杂度也为O(N),用于存储线段树的数据结构。
这个解法在处理大规模数据时具有较好的性能,但对于小规模数据,可能会有一些冗余。
对线段树不了解,可以通过 https://www.bilibili.com/video/BV1QT4y1Z7rR 视频来学习。
Java
java
import java.util.Scanner;
/**
* @author code5bug
*/
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] levels = new int[n];
for (int i = 0; i < n; i++) levels[i] = in.nextInt();
final int LEFT_BOUND = 1, RIGHT_BOUND = 100000;
// lt[i] 是 levels[0:i] 中小于 levels[i] 的个数
// gt[i] 是 levels[0:i] 中大于 levels[i] 的个数
int[] lt = new int[n], gt = new int[n];
SegmentTree segTree = new SegmentTree(LEFT_BOUND, RIGHT_BOUND);
for (int i = 0; i < n; i++) {
lt[i] = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
gt[i] = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
segTree.insert(levels[i]);
}
long rs = 0;
for (int i = 1; i < n - 1; i++) {
// 数组中总 [1, level[i] - 1] 总个数
int total_lt = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
// 数组中总 [level[i] + 1, RIGHT_BOUND] 总个数
int total_gt = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
rs += lt[i] * (total_gt - gt[i]);
rs += gt[i] * (total_lt - lt[i]);
}
System.out.println(rs);
}
}
/**
* 线段树
*/
class SegmentTree {
private int leftBound, rightBound;
private int[] data;
public SegmentTree(int leftBound, int rightBound) {
this.leftBound = leftBound;
this.rightBound = rightBound;
int size = (rightBound - leftBound + 1) * 4;
this.data = new int[size];
}
public void insert(int num) {
insert(0, leftBound, rightBound, num);
}
public void insert(int pos, int left, int right, int num) {
this.data[pos]++;
//线段树叶子节点
if (left == num && right == num) {
return;
}
int mid = left + (right - left) / 2;
if (num <= mid) {
insert(pos * 2 + 1, left, mid, num);
} else {
insert(pos * 2 + 2, mid + 1, right, num);
}
}
public int searchRange(int minNum, int maxNum) {
return searchRange(0, leftBound, rightBound, minNum, maxNum);
}
/**
* 数值范围查询
*
* @param pos
* @param left
* @param right
* @param minNum
* @param maxNum
* @return
*/
public int searchRange(int pos, int left, int right, int minNum, int maxNum) {
// 查询范围外,没有结果
if (minNum > right || maxNum < left) {
return 0;
}
// 叶子节点
if (left == right) {
return data[pos];
}
if (minNum <= left && maxNum >= right) {
// 完全被包含在查询范围内
return data[pos];
} else {
int mid = left + (right - left) / 2;
return searchRange(pos * 2 + 1, left, mid, minNum, maxNum) + searchRange(pos * 2 + 2, mid + 1, right, minNum, maxNum);
}
}
}
Python
python
class SegmentTree:
def __init__(self, left_bound, right_bound):
self.left_bound = left_bound
self.right_bound = right_bound
size = (right_bound - left_bound + 1) * 4
self.data = [0] * size
def insert(self, num):
self._insert(0, self.left_bound, self.right_bound, num)
def _insert(self, pos, left, right, num):
self.data[pos] += 1
if left == right:
return
mid = left + (right - left) // 2
if num <= mid:
self._insert(pos * 2 + 1, left, mid, num)
else:
self._insert(pos * 2 + 2, mid + 1, right, num)
def search_range(self, min_num, max_num):
return self._search_range(0, self.left_bound, self.right_bound, min_num, max_num)
def _search_range(self, pos, left, right, min_num, max_num):
if min_num > right or max_num < left:
return 0
if left == right:
return self.data[pos]
if min_num <= left and max_num >= right:
return self.data[pos]
mid = left + (right - left) // 2
return self._search_range(pos * 2 + 1, left, mid, min_num, max_num) + self._search_range(pos * 2 + 2, mid + 1, right, min_num, max_num)
def main():
n = int(input())
levels = list(map(int, input().split()))
LEFT_BOUND, RIGHT_BOUND = 1, 100000
lt = [0] * n
gt = [0] * n
seg_tree = SegmentTree(LEFT_BOUND, RIGHT_BOUND)
for i in range(n):
lt[i] = seg_tree.search_range(LEFT_BOUND, levels[i] - 1)
gt[i] = seg_tree.search_range(levels[i] + 1, RIGHT_BOUND)
seg_tree.insert(levels[i])
rs = 0
for i in range(1, n - 1):
total_lt = seg_tree.search_range(LEFT_BOUND, levels[i] - 1)
total_gt = seg_tree.search_range(levels[i] + 1, RIGHT_BOUND)
rs += lt[i] * (total_gt - gt[i])
rs += gt[i] * (total_lt - lt[i])
print(rs)
if __name__ == "__main__":
main()
C++
cpp
#include <iostream>
#include <vector>
using namespace std;
class SegmentTree {
private:
int leftBound, rightBound;
vector<int> data;
public:
SegmentTree(int left, int right) : leftBound(left), rightBound(right) {
int size = (rightBound - leftBound + 1) * 4;
data.resize(size, 0);
}
void insert(int num) {
insert(0, leftBound, rightBound, num);
}
void insert(int pos, int left, int right, int num) {
data[pos]++;
if (left == num && right == num) {
return;
}
int mid = left + (right - left) / 2;
if (num <= mid) {
insert(pos * 2 + 1, left, mid, num);
} else {
insert(pos * 2 + 2, mid + 1, right, num);
}
}
int searchRange(int minNum, int maxNum) {
return searchRange(0, leftBound, rightBound, minNum, maxNum);
}
int searchRange(int pos, int left, int right, int minNum, int maxNum) {
if (minNum > right || maxNum < left) {
return 0;
}
if (left == right) {
return data[pos];
}
if (minNum <= left && maxNum >= right) {
return data[pos];
} else {
int mid = left + (right - left) / 2;
return searchRange(pos * 2 + 1, left, mid, minNum, maxNum) +
searchRange(pos * 2 + 2, mid + 1, right, minNum, maxNum);
}
}
};
int main() {
int n;
cin >> n;
const int LEFT_BOUND = 1, RIGHT_BOUND = 100000;
vector<int> levels(n);
for (int i = 0; i < n; ++i) {
cin >> levels[i];
}
vector<int> lt(n), gt(n);
SegmentTree segTree(LEFT_BOUND, RIGHT_BOUND);
for (int i = 0; i < n; ++i) {
lt[i] = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
gt[i] = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
segTree.insert(levels[i]);
}
long long result = 0;
for (int i = 1; i < n - 1; ++i) {
int total_lt = segTree.searchRange(LEFT_BOUND, levels[i] - 1);
int total_gt = segTree.searchRange(levels[i] + 1, RIGHT_BOUND);
result += lt[i] * (total_gt - gt[i]) + gt[i] * (total_lt - lt[i]);
}
cout << result << endl;
return 0;
}
相关练习题
题号 | 题目 | 难易 |
---|---|---|
LeetCode 307 | 307. 区域和检索 - 数组可修改 | 中等 |
LeetCode 2276 | 2276. 统计区间中的整数数目 | 困难 |
🙏整理题解不易, 如果有帮助到您,请给点个赞 ❤️ 和收藏 ⭐,让更多的人看到。🙏🙏🙏