链接:https://ac.nowcoder.com/acm/problem/19913
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。
输入描述:
第一行为两个正整数n和b ,第二行为1~n 的排列。
输出描述:
输出一个整数,即中位数为b的连续子序列个数。
示例1
输入
7 4 5 7 2 4 3 1 6
输出
4
由于是 1~n 的一个排列,所以不用担心数字重复的问题。因为是一个奇数长度的连续子区间,那么中位数这个数一定在这个区间里,且这个区间大于中位数的数等于小于中位数的数。
由于是求中位数,所以除了 b 以外的数的具体大小并不重要,重要的是该数比 b 大还是比 b 小。我们用 a[i] = -1 代表该数比 b 小,用 a[i] =1 代表该数比 b 大。
由连续区间,很容易让人联想到前缀和。在这题中,我们使用前缀和来表示从当前数到 b 的区间中,比 b 大的数和比 b 小的数的个数的差。由于这个差可能为负,所以我们可以用map存。
这样的话,该问题转化为,求有多少个区间的和为0,并且包含数字b的位置。
然后从b的位置pos往前计算和sum,然后看pos右遍有没有出现-sum,看前面是不是已经存过了,存过了就加起来前面存的个数即可。
具体做法:
在读入的时候,将大于中位数的数据赋值 1 ,小于中位数的数据赋值 1 ,等于中位数的数据赋值 0 ,并记录中位数的下标,左右遍历统计即可。
左遍历的时候如果区间和为sum,并利用 map 查询右边区间和为 -sum 的情况,如果右边有区间和为-sum ,则可以与左边的sum 配对。
注意:中位数b单独一个数字也是一个区间。
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 #define pb push_back 4 #define mst(a) memset(a,0,sizeof(a)) 5 const int INF = 0x3f3f3f3f; 6 const double eps = 1e-8; 7 const int mod = 1e9+7; 8 const int maxn = 1e5+10; 9 using namespace std; 10 11 int a[maxn]; 12 map<int,int> mp; 13 14 int main() 15 { 16 #ifdef DEBUG 17 freopen("sample.txt","r",stdin); //freopen("data.out", "w", stdout); 18 #endif 19 20 int n,m; 21 scanf("%d %d",&n,&m); 22 int pos; 23 for(int i=1;i<=n;i++) 24 { 25 scanf("%d",&a[i]); 26 if(a[i]>m) a[i]=1; 27 else if(a[i]<m) a[i]=-1; 28 else pos=i, a[i]=0; 29 } 30 mp[0]++; 31 int sum = 0; 32 for(int i=pos+1;i<=n;i++) 33 { 34 sum+=a[i]; 35 mp[sum]++; 36 } 37 int ans = 0; 38 sum = 0; 39 for(int i=pos;i>=1;i--) 40 { 41 sum += a[i]; 42 if(mp.count(-sum)) ans += mp[-sum]; 43 } 44 printf("%d ",ans); 45 46 return 0; 47 }
-