[CQOI2009]中位数图(思维、前缀和)

 

链接:https://ac.nowcoder.com/acm/problem/19913
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制: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\n",ans);
45     
46     return 0;
47 }

 

 

 

 

 

-

posted @ 2020-07-04 12:09  jiamian22  阅读(680)  评论(0)    收藏  举报