「CQOI2009」中位数

「CQOI2009」中位数

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

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

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

[small_{ ext{左}}+small_{ ext{右}}=big_{ ext{左}}+big_{ ext{右}} ]

接下来移项:

[small_{ ext{左}}-big_{ ext{左}}=big_{ ext{右}}-small_{ ext{右}} ]

那么我们就可以用桶来维护,具体方法如下:
在对一种边界情况进行处理时(以向右拓展为例),我们开一个桶 (tong[i]) 表示有多少种 (big-small) 等于 (i)。(为了防止数组越界可以同时加上一个数)
然后在对另一边进行处理时干一件这样的事: (ans ext{+=} 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
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/zsbzsb/p/12246862.html