题目
题目描述: 给出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; }