#P2729. 第2题-小美的中位数
-
1000ms
Tried: 159
Accepted: 43
Difficulty: 4
所属公司 :
美团
时间 :2025年3月22日-开发岗
-
算法标签>双指针
第2题-小美的中位数
问题分析
本题要求我们找到一个排列 p 中的所有“好数组”区间的个数。具体来说,给定一个排列 p 和多个查询,每个查询包含一个长度为奇数的区间 [l, r],并要求判断区间内的中位数是否满足“好数组”的条件:该区间的中位数恰好是区间的第 (r - l + 1) // 2 + 1 的位置上的数字,并且此中位数在该区间内不需要进行排序后也符合中位数的定义。
“好数组”的定义
“好数组”要求数组的中位数即为该区间的第 (n + 1) / 2 个元素(即中间元素)。例如,若数组的长度为 5,那么第 3 个元素就是中位数。
解题思路
-
中位数的定义: 对于一个长度为奇数的子数组,数组的中位数是该数组排序后位于中间位置的元素。例如,对于长度为 5 的子数组
p[l..r],它的中位数就是p[(l+r)//2]。 -
“好数组”区间的判断: 对于每个子区间,我们需要检查其是否是“好数组”。根据题目要求,找到所有长度为奇数的子数组,并且要判断这个子数组的中位数是否符合要求。
-
解法思路: 我们从每一个元素出发,尝试扩大窗口,检查窗口内的中位数是否符合“好数组”的要求。具体步骤如下:
- 对每一个可能的子数组,检查其是否为好数组。
- 具体做法是对于每一个中位数,判断其是否满足条件,并用双指针的方式,计算所有符合条件的区间。
复杂度分析
- 两层循环,复杂度为O(n2)
代码实现
Python代码实现
def solve():
t = int(input())
for _ in range(t):
n = int(input())
p = list(map(int, input().split()))
result = 0
for i in range(n):
d = 0
total = 0
for k in range(1, min(i, n -1 - i) + 1):
left = i - k
right = i + k
total += 1 if p[left] > p[i] else -1
total += 1 if p[right] > p[i] else -1
if total == 0:
d += 1
result += d + 1
print(result)
# 输入调用
solve()
Java代码实现
import java.io.*;
import java.util.*;
public class Main {
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());
while (T-- > 0) {
st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int[] p = new int[n + 1];
st = new StringTokenizer(br.readLine());
// 读取排列p
for (int i = 1; i <= n; i++) {
p[i] = Integer.parseInt(st.nextToken());
}
int rs = 0;
// 计算每个位置作为中位数的子区间数量
for (int i = 1; i <= n; i++) {
int d = 0;
int sum = 0;
// 计算以i为中位数的子区间
for (int k = 1; k <= Math.min(i-1, n - i); k++) {
int left = i - k;
int right = i + k;
// 判断左右两侧元素是否符合“好数组”的条件
sum += (p[left] > p[i]) ? 1 : -1;
sum += (p[right] > p[i]) ? 1 : -1;
// 如果当前区间满足条件,则计数
if (sum == 0) {
d++;
}
}
// 每个位置都要加1
rs += d + 1;
}
// 输出每个测试的结果
System.out.println(rs);
}
}
}
C++代码实现
#include <iostream>
#include <vector>
using namespace std;
void solve() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> p(n);
for (int i = 0; i < n; i++) {
cin >> p[i];
}
int result = 0;
for (int i = 0; i < n; i++) {
int d = 0;
int total = 0;
for (int k = 1; k <= min(i, n - i - 1); k++) {
int left = i - k;
int right = i + k;
total += (p[left] > p[i]) ? 1 : -1;
total += (p[right] > p[i]) ? 1 : -1;
if (total == 0) {
d++;
}
}
result += d + 1;
}
cout << result << endl;
}
}
int main() {
solve();
return 0;
}
题目内容
众所周知,一个长度为n,n为奇数的数组的中位数是其排序后下标为2n+1的数字。
但小美发现,有些长为奇数n的数组即使不排序,其下标为2n+1的数字依然是其数组中位数,他将满足这样性质的数组成为“好数组"。
现在小美有一个长度为n的排列p,他想知道p中有多少个连续的区间组成的数组都是"好数组"。
(即有多少对l,r(1≤l≤r≤n),同时r−l+1是奇数,满足Pl,Pl+1,⋅⋅,Pr是一个好数组。)
输入描述
每个测试文件内都包含多组测试数据。
第一行一个正整数T(I≤T≤1000),表示测试数据的组数。
接下来对于每组测试数据,输入包含两行。
第一行两个整数n(1≤n≤10000),表示排列p的长度。
第二行n个整数pi(1≤pi≤n),表示排列P。
(保证输入是一个排列。)
(保证所有测试数据中n的总和不超过10000)
输出描述
输出包含 T行,每行一个正整数,表示合法的l,r对数。
样例1
输入
1
5
3 2 1 4 5
输出
7
说明
好数组的区间有: [1,1], [2,2], [3,3], [4,4], [5,5], [1,3], [3,5]这 7个。