0%

区间中位数与 CF641D

昨天打了 CF614,依旧不太顺心,被 D 卡死了。比赛时一直以为是我区间中位数求的有问题,比赛后才发现是我得出的结论有问题。。

区间中位数

若将中位数定义为区间排序后第 $\lfloor (n + 1)/2 \rfloor$ 个数,定义数组 $b$,

则区间中位数大于等于 $k$ 等价于 $\sum_{i=l}^r b_i > 0$。
利用树状数组即可在 $O(n\log n)$ 时间内求出中位数大于等于 $k$ 的子区间数量。

若将中位数定义为区间排序后第 $\lfloor n / 2 \rfloor + 1$ 个数,那么定义数组 $b$,

则区间中位数小于等于 $k$ 等价于 $\sum_{i=l}^r b_i < 0$。

当然也可以将 $b$ 定义为

则区间中位数小于等于 $k$ 等价于 $\sum_{i=l}^r b_i > 0$。

CF614 D

让我们回到这道题。题意大概是给一个长度为 $n(n \leq 10000)$ 的数组和一个整数 $k$,可以无限次执行以下操作:选择一个子区间,让这个子区间内的所有数等于这个区间内的中位数,其中中位数定义为子区间内下标为 $\lfloor (n + 1)/2 \rfloor$ 对应的数。问最终能否把原始数组全部变成 $k$。

这道题有这样一个结论:当且仅当存在一个长度大于等于 2 的区间使得区间内中位数大于等于 $k$ 并且原数组至少存在一个 $k$ 时,可以满足要求。可以简单的证明一下:首先我们能得到一些简单的结论,当区间内存在相邻的两个 $k$ 时,我们便可以不断向左右扩张使得整个区间都变为 $k$。当存在与 $k$ 相邻的大于 $k$ 的数时,那么便可以将这个数变为 $k$。现在如果存在一个区间中位数大于等于 $k$,若恰好等于 $k$,则可以将这个区间都变成 $k$ 从而满足条件;若大于 $k$,则可以将整个区间都变成大于 $k$,然后可以将区间不断延伸直到与 $k$ 相邻,从而也可以将整个区间变成 $k$。这就证明了必要性。充分性显然,因为如果不存在度大于等于 2 的区间使得区间内中位数大于等于 $k$,那么就不可能通过操作增加大于等于 $k$ 的数。

比赛时我只考虑了中位数等于 $k$ 的情况,疯狂 WA。这就是把充分条件当成充要条件的下场。。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define fl first
#define fr second
using namespace std;

typedef long long LL;
typedef pair<int, int> pii;
const int N = 200000 + 10;

int n, k, a[N], b[N];
LL tr[N];

void Add(int x, int a)
{
for (; x <= n * 2 + 1; x += x & -x)
tr[x] += a;
}

LL Query(int x)
{
LL res = 0;
for (; x > 0; x -= x & -x)
res += tr[x];
return res;
}

int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d%d", &n, &k);
for (int i = 0; i <= n * 2 + 1; i++) tr[i] = 0;
int num = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
if (a[i] >= k) b[i] = 1;
else b[i] = -1;
if (a[i] == k) num++;
}
if (num == 0)
{
puts("no");
continue;
}
if (n == 1)
{
if (b[1] == 1) puts("yes");
else puts("no");
continue;
}
LL res = 0, sum = 0;
Add(n + 1, 1);
for (int i = 1; i <= n; i++)
{
sum += b[i];
res += Query(sum + n);
Add(sum + n + 1, 1);
}
for (int i = 1; i <= n; i++)
if (b[i] == 1) res--;
if (res > 0) puts("yes");
else puts("no");
}
return 0;
}