题目
题目描述: 给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
第一行为两个正整数n和b ,第二行为1~n 的排列。
对于 30% 的数据中,满足 n≤100;
对于 60% 的数据中,满足 n≤1000;
对于 1100% 的数据中,满足n≤100000,1≤b≤n。
输出一个整数,即中位数为b的连续子序列个数。
解析
这道题我一开始还以为是啥尺取题,没想到就是一道思维题。。。而且我还没看到这是一个1~n的排列orz
首先我们来看看题目:
- 既然说要中位数的话,而且题目说了一定是一个奇数长子串,我们就知道:中位数这个数一定在这个子串里面。
- 然后子串一定是:大于中位数的数等于小于中位数的数。
算法操作:
- 我一开始还想拿两个变量来存,看了大佬的题解之后发现还能这样简单操作:将数组中大于中位数的数设置为1,小于的设置为-1。
- 栗子:3为中位数,{5,2,3,8,4,6,7,1}->{1,-1,3,1,1,1,1,-1}。
- 这样呢,从中间向两边看,先看左边和右边,用sum统计和值,sum==0的时候就说明1和-1数量一样多了。
- 左右都有的情况怎么看呢?你就要加一个数组(num数组),
统计左边的时候,每种sum的情况(也就是对数的删减情况,下面举例),右边情况求互补的就行了。 - 栗子:就拿上面这个:{1,-1,3,1,1,1,1,-1},我们先从3往左边看,碰到第一个是-1,sum为-1,就让num[n - 1]++。
碰到第二个是1,求和之后sum是0,就让num[n + 0]++(总体也就是num[n - sum])。
然后统计右边的时候,如果碰到sum = 1时,就可以加上num[n - 1]的值了对吧(总体也就是ans += num[n + sum])。
打代码咯:
- 先初始化数组,按上面说的换成1,-1,中位数这样的。
- 然后观测左边。
- 然后观测右边,顺带就可以统计两边的情况。
- 看我代码咯~
AC代码
#include <iostream>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//代码预处理区
const int MAX = 1e5 + 7;
int n, b, a[MAX], num[MAX << 1];
//全局变量区
int main() {
cin >> n >> b;
int pos = 0;//标记中位数位置
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] > b) a[i] = 1;
else if (a[i] < b) a[i] = -1;
else pos = i;
}
int sum = 0, ans = 1;
for (int i = pos - 1; i >= 1; i--) {
sum += a[i];
num[n + sum]++;
if (!sum) ans++;//中位数+左边
}
sum = 0;
for (int i = pos + 1; i <= n; i++) {
sum += a[i];
ans += num[n - sum];//左边与右边搭配的情况
if (!sum) ans++;//中位数+右边
}
cout << ans << endl;
return 0;
}
京公网安备 11010502036488号