1. 程式人生 > >bzoj 1303 中位數 題解

bzoj 1303 中位數 題解

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=