bzoj 1303 中位数 题解

25 篇文章 0 订阅
7 篇文章 0 订阅

4.中位数

(median.pas/c/cpp)

【问题描述】

给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b。中位数是指把所有元素从小到大排列后,位于中间的数。

【输入】

第一行为两个正整数n和b ,第二行为1~n 的排列。

【输出】

输出一个整数,即中位数为b的连续子序列个数。

 【样例输入】

7 4
5 7 2 4 3 1 6

 【样例输出】

4

 【样例解释】

分别是子序列{4},{7,2,4},{5,7,2,4,3},{5,7,2,4,3,1,6}

 【数据范围】

N<=100000


其实还是蛮好上手的一道题,显然我们符合要求的序列:

1、一定包含我们的中位数,

2、而且序列内大于中位数的个数等于小于中位数的个数相等(中位数定义可知=。=)

3、序列长度为奇数

我们设中位数在序列中的位置为t

对于1~t-1

a[i],a[i+1],a[i+2]......,a[t-1]

我们用l_min[i]记录a[i]~a[t] 中比中位数小的个数,用l_max[i]记录a[i]~a[t]中比中位数大的个数

同理,对于a[t+1],a[t+2],...,a[n]

我们用r_min[j]记录a[t]~a[n]中比中位数小的个数,用r_max[j]记录a[t]~a[n]中比中位数大的个数

那么满足条件的序列a[i]~a[j]一定有

1.i<=t<=j

2.j-i>=2

3.l_min[i]+r_min[j]=l_max[i]+r_max[j] (i<t<j) 

   l_min[i]=l_max[j](i<t=j)

   r_min[i]=r_max[j](t=i<j)

然后分水岭来了=w=

方法一:处理完后枚举1~t-1,t+1~n,满足上述三条要求的加答案,期望得分70

AC方法:由于两边同时处理会TLE,所以我们考虑一边的min和max一起处理

于是l_min[i]+r_min[j]=l_max[i]+r_max[j]  变形(看好了我要变了!!) l_min[i]-l_max[i]=r_max[j]-r_min[j] 

即  l_min[i]-l_max[i]=-(r_min[j]-r_max[j] )

那么我们只需要处理出 l_min[i]-l_max[i]=k的序列个数fl[k]和 r_min[j]-r_max[j]=k的序列个数fr[k]

然后ans:=Σfl[k]*fr[-k] (-n<=k<=n),最坏时间复杂度O(n),完美=w=

PS:考试的时候由于我是从中位数的位置向两边同时去更新,导致一边更新完成时另一边还没有完成更新,听取wa声一片QAQ

在更新数值的时候,如果是从中间向两端同时更新,在更新后要判断两边是否全部完成更新,不要留下有一边只更新一部分的情况

注意对不存在中位数m的特判

代码

var
        n,m,t,ans            :longint;
        a,lmin,lmax,rmin,rmax:array[0..100010] of longint;
        t1,t2                :array[-100005..100005] of longint;
        i,j                  :longint;
begin
   read(n,m);
   for i:=1 to n do read(a[i]);
   for i:=1 to n do if a[i]=m then break;
   t:=i;
   lmin[t]:=0;lmax[t]:=0;rmin[t]:=0;rmax[t]:=0;
   //
   for i:=t-1 downto 1 do
   begin
      lmin[i]:=lmin[i+1];
      lmax[i]:=lmax[i+1];
      if a[i]<m then inc(lmin[i]) else inc(lmax[i]);
      if lmin[i]=lmax[i] then inc(ans);
   end;
   //
   for i:=t+1 to n do
   begin
      rmax[i]:=rmax[i-1];
      rmin[i]:=rmin[i-1];
      if a[i]>m then inc(rmax[i]) else inc(rmin[i]);
      if rmax[i]=rmin[i] then inc(ans);
   end;
   //
   for i:=1 to t-1 do inc(t1[lmin[i]-lmax[i]]);
   for i:=t+1 to n do inc(t2[rmin[i]-rmax[i]]);
   for i:=-n to n do inc(ans,t1[i]*t2[-i]);
   if t=0 then writeln(0) else writeln(ans+1);
end.

附上一个中位数的性质=w=: 有一列数X1,X2,X3,...,Xn,

f(x)=|X1-x|+|X2-x|+|X3-x|+...+|Xn-x|,当x=数列中位数时,f(x)最小

——by Eirlys

转载请注明出处=w=



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值