题目传送门

前言

感谢@HwH@chenyilei同学提供的帮助!

应该是本题最优雅、简洁易懂的题解。

尤菈:对,就是要优雅!

嗯嗯,那我们开始!

解析

首先我们看到这个题说的平均数不小于$k$,应该很容易想到将每个数都减去$k$,然后只要考虑大于零即可。

既然是大于零,一个正数除以一个正数还是正数,就不必去除了,只要考虑区间和大于零。

所以为了提高效率我们很显然要用到一个前缀和优化。

然后考虑如何找出有多少段区间和大于零即为答案。

考虑枚举右端点。

容易发现,对于每一个右端点$i$,他前面所有$sum[j]$小于等于$sum[i]$,都可以组成一段区间和大于等于0的连续子序列,也就是题目中所说的平均数$\geq k$的连续子序列。同时,别忘了如果这个$sum[i]$本身$\geq 0$,那么序列$1\sim k$也是一段平均数$\geq k$的连续子序列,此时应该$++ans$。

枚举右端点计算每个右端点对答案的贡献,加和即可。

那么,如何求一个右端点$i$前面所有$sum[j]$小于等于$sum[i]$的$j$有几个呢?

我们可以将$sum[i]$逐个加入,每一个$sum[i]$加入时,他前面有多少小于等于$sum[i]$的,那这个$sum[i]$对于答案的贡献,也就是右端点$i$前面所有$sum[j]$小于等于$sum[i]$的$j$的数量,就是多少。(想想为什么,虽然很明显)

嗯,思路就是如此简单,那么如何实现呢?

emm……我想想……

不停加入数,然后查询它是目前的第几名……

嗯!我想到了!一定是优雅的平衡树!

对,我们在这里为了优雅,尽量使main函数简洁易懂,使用易于封装漂亮的平衡树实现。

我在这里使用的是优雅的FHQ-Treap

尤菈屡屡探头

代码

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <bits/stdc++.h>
#define INF 999999999
#define arand() rand() % 114514
#define mrg() rot = merge(X, Y)

using namespace std;

inline long long read()
{
long long x = 0;
int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void write(const long long &x)
{
if (!x)
{
putchar('0');
return;
}
char f[100];
long long tmp = x;
if (tmp < 0)
{
tmp = -tmp;
putchar('-');
}
long long s = 0;
while (tmp > 0)
{
f[s++] = tmp % 10 + '0';
tmp /= 10;
}
while (s > 0)
{
putchar(f[--s]);
}
}

const long long maxN = 1000090;
long long totN;
long long totK;
long long nums[maxN];
long long sum[maxN];
priority_queue<long long> Q;

struct Node
{
long long lch, rch;
long long val, siz;
long long rnd;
} nodes[maxN];

long long X, Y, Z;
long long rot;
long long cnt;
long long totANS;

void update(long long nowX)
{
nodes[nowX].siz = nodes[nodes[nowX].lch].siz + nodes[nodes[nowX].rch].siz + 1;
}

long long merge(long long Atree, long long Btree)
{
if ((!Atree) || (!Btree))
{
return Atree + Btree;
}
else if (nodes[Atree].rnd < nodes[Btree].rnd)
{
nodes[Atree].rch = merge(nodes[Atree].rch, Btree);
update(Atree);
return Atree;
}
else
{
nodes[Btree].lch = merge(Atree, nodes[Btree].lch);
update(Btree);
return Btree;
}
}

void split(long long splitX, long long K, long long &Xtree, long long &Ytree)
{
if (!splitX)
{
Xtree = Ytree = 0;
return;
}
else
{
if (nodes[splitX].val <= K)
{
Xtree = splitX;
split(nodes[splitX].rch, K, nodes[splitX].rch, Ytree);
}
else
{
Ytree = splitX;
split(nodes[splitX].lch, K, Xtree, nodes[splitX].lch);
}
update(splitX);
}
}

void del(long long delX)
{
split(rot, delX, X, Z);
split(X, delX - 1, X, Y);
Y = merge(nodes[Y].lch, nodes[Y].rch);
rot = merge(merge(X, Y), Z);
}

void insert(long long nowX)
{
split(rot, nowX, X, Y);
++cnt;
nodes[cnt].lch = nodes[cnt].rch = 0;
nodes[cnt].val = nowX;
nodes[cnt].siz = 1;
nodes[cnt].rnd = arand();
rot = merge(merge(X, cnt), Y);
}

long long thK(long long nowX)
{
split(rot, nowX , X, Y);
return nodes[X].siz - 1;
}

int main()
{
totN = read();
totK = read();
for (long long i = 1; i <= totN; ++i)
{
nums[i] = read() - totK;
sum[i] = nums[i] + sum[i - 1];
}
for (int i = 1; i <= totN; ++i)
{
insert(sum[i]);
totANS += thK(sum[i]) ;
mrg();
if (sum[i] >= 0)
{
++totANS;
}
}
write(totANS);
return 0;
} //Thomitics Code

是不是!看一眼main函数,一下子就懂了怎么做!嗯!优雅!

尤菈看着代码,满意地笑了