「CQOI2009」中位数

「CQOI2009」中位数

传送门
这道题将会用到一点桶的思想。
首先我们可以在排列中先找到 \(b\) 的位置(找不到的话就直接输出 \(0\))。
然后我们从 \(b\) 的位置(设为 \(p\))开始拓展,容易发现有三种情况:

  • \(b\) 在子段左边界
  • \(b\) 在子段右边界
  • \(b\) 在子段中间位置

我们很容易想到,对于 \(b\) 在子段边界的情况可以直接扫描,记录一下小于 \(b\) 的数和大于 \(b\) 的数的个数即可。
对于 \(b\) 在序列中间的情况可以这样做:
类比我们处理其他两种情况的方法,我们都是记录了小于 \(b\) 和大于 \(b\) 的数的个数(简记为 \(small\)\(big\)
\(small=big\) 时,意味着当前 \(b\) 就是中位数,我们就可以将答案加一。
同样的对于这种情况,我们不难得到下面这个式子:

\[small_{\text{左}}+small_{\text{右}}=big_{\text{左}}+big_{\text{右}} \]

接下来移项:

\[small_{\text{左}}-big_{\text{左}}=big_{\text{右}}-small_{\text{右}} \]

那么我们就可以用桶来维护,具体方法如下:
在对一种边界情况进行处理时(以向右拓展为例),我们开一个桶 \(tong[i]\) 表示有多少种 \(big-small\) 等于 \(i\)。(为了防止数组越界可以同时加上一个数)
然后在对另一边进行处理时干一件这样的事: \(ans \text{+=} tong[small - big]\)
这样我们就完成了的三种情况的方案数统计,可以见代码。
参考代码:

/*--------------------------------
  Code name: A.cpp
  Author: The Ace Bee
  This code is made by The Ace Bee
--------------------------------*/
#include <cstdio>
#define rg register
#define file(x)									\
	freopen(x".in", "r", stdin);				\
	freopen(x".out", "w", stdout);
const int $ = 100010;
inline int read() {
	int s = 0; bool f = false; char c = getchar();
	while (c < '0' || c > '9') f |= (c == '-'), c = getchar();
	while (c >= '0' && c <= '9') s = (s << 3) + (s << 1) + (c ^ 48), c = getchar();
	return f ? -s : s;
}
int a[$], tong[$ * 5], OFF = $ * 2;
int main() {
//	file("A");
	int n = read(), k = read(), p = 0;
	for (rg int i = 1; i <= n; ++i)
	{ a[i] = read(); if (a[i] == k) p = i; }
	if (p == 0) return puts("0"), 0;
	int ans = 1, s1 = 0, b1 = 0, s2 = 0, b2 = 0;
	for (rg int i = p + 1; i <= n; ++i) {
		if (a[i] > k) ++b2; else ++s2;
		++tong[b2 - s2 + OFF];
		if (s2 == b2) ++ans;
	}
	for (rg int i = p - 1; i >= 1; --i) {
		if (a[i] > k) ++b1; else ++s1;
		ans += tong[s1 - b1 + OFF];
		if (s1 == b1) ++ans;
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2020-01-31 21:39  Sangber  阅读(136)  评论(0编辑  收藏  举报