1. 程式人生 > >[CQOI2009] 中位數

[CQOI2009] 中位數

不錯的思維題

傳送門:$>here<$


題意:給出一個N的排列,求出其中有多少個連續子段的中位數是b

資料範圍:$N \leq 100000$


 $Solution$

先考慮中位數的意義:一個序列中,大於它的與小於它的一樣多。而由於中位數已經確定,所以最終的序列一定包含它所在的那個位置。

設$$c[i]=\begin{cases}0 & \text{} a[i]==b \\ 1 & \text{} a[i]>b \\ -1 & \text{} a[i]<b \end{cases}$$

於是如果我們統計一個$sum[i]$表示$c[i]$的字首和,在開一個桶表示左側右側對應的個數。就可以得到答案

$$ans = \sum\limits_{i=-N}^{+N}bl[i]*br[-i]$$

注意下標不能為負數

反思

對於無論是平均數還是中位數,對於計算機來說都不好處理。此時最好的辦法就是還原為最簡單的求和問題

$my \ code$

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const
int MAXN = 100010; const int INF = 1061109567; const int ad = 1e5; inline int Max(const int a, const int b){ return (a > b) ? a : b; } inline int Min(const int a, const int b){ return (a < b) ? a : b; } inline int read(){ int x = 0; int w = 1; register char c = getchar(); for(; c ^ '-
' && (c < '0' || c > '9'); c = getchar()); if(c == '-') w = -1, c = getchar(); for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w; } int N,B,pos,Ans; int a[MAXN],c[MAXN],sum[MAXN],bl[MAXN*2],br[MAXN*2]; int main(){ N = read(), B = read(); for(int i = 1; i <= N; ++i){ a[i] = read(); if(a[i] < B) c[i] = -1; if(a[i] > B) c[i] = 1; if(a[i] == B) pos = i; sum[i] = sum[i-1] + c[i]; } for(int i = 0; i < pos; ++i){ bl[sum[pos-1]-sum[i]+ad]++; } for(int i = pos; i <= N; ++i){ br[sum[i]-sum[pos]+ad]++; } for(int i = -N; i <= N; ++i){ Ans += bl[i+ad] * br[-i+ad]; } printf("%d", Ans); return 0; }