本题解思路来自于 spoonjunxi

A

题目描述

给定三个数$a,b,c$,判断$b$是否在$[a,c]$之间,如果是输出Yes,否则输出No

题目分析

直接判断即可。

$Code$

1
2
3
4
5
int a, b, c;
cin >> a >> b >> c;

if(b >= a && b <= c || b <= a && b >= c) puts("Yes");
else puts("No");

$ \ $
$ \ $

B

题目描述

给定一个矩形,求出矩形中两个o之间的距离。

题目分析

曼哈顿距离公式。

$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
int n, m;
int a[4];
int idx;

int main()
{
cin >> n >> m;

char c;

for(int i = 1; i <= n; i ++ )
for(int j = 1; j <= m; j ++ )
{
cin >> c;
if(c == 'o')
{
a[idx ++ ] = i;
a[idx ++ ] = j;
}
}

cout << abs(a[0] - a[2]) + abs(a[1] - a[3]) << endl;

return 0;
}

$ \ $
$ \ $

C

题目描述

给定一个multiset——S,最开始置为空。

给定Q个询问

  1. 1 x:向S中插入$x$。
  2. 2 x c:在S中删除$min(c, x的个数)$个$x$。
  3. 输出S中最大值$-$最小值。

题目分析

对于此题,题目要求我们使用multiset,但为了方便,我们使用set,用数组来统计每个$x$出现的次数。

由于$0 \le x \le 10^9$,用数组开会浪费空间。

注意到询问次数$1 \le Q \le 2 \times 10^5$,最多$2 \times 10^5$,启发我们可以使用unordered_map来记录每个数出现的次数。

unordered_map<int, int> mp;

$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
int main()
{
int T;
cin >> T;

set<int> s;
unordered_map<int, int> mp;

while(T -- )
{
int t, x, c;
cin >> t;
if(t == 1)
{
cin >> x;
if(mp[x] == 0) s.insert(x); // 当mp[x] == 0时说明s中一个x都没有,则加入s中
mp[x] ++ ;
}
else if(t == 2)
{
cin >> x >> c;
if(mp[x] > c) mp[x] -= c; // 如果数量够减就减
else // 不够则置为0,同时将s中的x删除,因为此时x的数量为0
{
mp[x] = 0;
s.erase(x);
}
}
else
{
cout << *s.rbegin() - *s.begin() << endl;
}
}

return 0;
}

$ \ $
$ \ $

D

题目描述

求出$1$到$N$中不是$A$的倍数,或者不是$B$的倍数的总数和是多少。

题目分析

利用容斥原理。

如图,总数和就是大圈减去两个小圈在加上两个小圈的重合部分。

abc_d.png

对于A的倍数的总和:

$q_1 = N / A$:$1$到$N$中$A$的倍数的数有多少个。

假设$N$ = 10,$A$ = 3。

$q_1$ = 3,说明有三个数是$A$的倍数,分别为$3、6、9$。

如何求它们的和呢?

我们发现它们呈现等差数列的现象,公差为$a$,有$q_1$个数字。

于是$suma = \frac{q_1 (q_1 + 1)}{2} \times A$

同理我们可以求出:

B的倍数的总和:

$q_2 = N / B$

$sumb = \frac{q_2(q_2 + 1)}{2} \times B$

A,B最小公倍数的倍数的总和(既是A的倍数,也是B的倍数的总和):

$q_3 = N / lcm(A,B)$

$sumc = \frac{q_3(q_3 + 1)}{2} \times lcm(A,B)$

所有数的总和:

$\frac{N (N + 1)}{2}$

于是答案就是$\frac{N (N + 1)}{2} - suma - sumb + sumc$

注意开long long!!!

$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
typedef long long LL;

LL gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}

LL lcm(int a, int b)
{
return a / gcd(a, b) * b;
}

int main()
{
LL n, a, b;
cin >> n >> a >> b;

LL q1 = n / a;
LL q2 = n / b;
LL t = lcm(a, b);
LL q3 = n / t;

LL suma = q1 * (q1 + 1) / 2 * a;
LL sumb = q2 * (q2 + 1) / 2 * b;
LL sumc = q3 * (q3 + 1) / 2 * t;

LL sum = suma + sumb - sumc;

LL ans = n * (n + 1) / 2 - sum;
cout << ans << endl;

return 0;
}

$ \ $
$ \ $

E

题目描述

有多少个长度为$N$的 $A$数组。

满足

  1. 每一位小于$M$
  2. 相邻两位的的绝对值之差小于$K$

答案MOD$998244353$

题目分析

由于每一位与前面一位有关系,启发我们可以使用动态规划

abc_e.png

优化

在状态计算中,我们可以通过前缀和的思想进行优化。

求$\sum_{u = 1}^{j - k} f[i][u]$

可以开个前缀和数组$s$,用来记录上一层所有情况的前缀和。

$$\sum_{u = 1}^{j - k} f[i][u] = s[j - k]$$

$$\sum_{u = j + k}^{n} f[i][u] = s[n] - s[j + k - 1]$$

注意

$k$的取值可能为0,此时注意特判一下即可。

$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
typedef long long LL;

LL n, m, k;
LL s[N];
LL f[N][N];

int main()
{
cin >> n >> m >> k;

for(int i = 1; i <= m; i ++ ) f[1][i] = 1; // 初始化,第一位填什么都只有一种情况

for(int i = 2; i <= n; i ++ ) // 从第二位开始计算
{
for(int j = 1; j <= m; j ++ ) // 处理前缀和数组
s[j] = (s[j - 1] + f[i - 1][j]) % MOD;

for(int j = 1; j <= m; j ++ )
{
int a = j - k;
int b = j + k;

// 当k = 0时,则说明上一层的所有数都满足要求
if(k == 0) f[i][j] = (f[i][j] + s[m]) % MOD;
else
{
if(a > 0) f[i][j] = (f[i][j] + s[a]) % MOD; // 如果j - k不是负数
if(b <= m) f[i][j] = (f[i][j] + s[m] - s[b - 1] + MOD) % MOD; // 如果j + k不大于m
// 为什么要加MOD在模MOD呢???
// 为什么要加MOD在模MOD呢???
// 为什么要加MOD在模MOD呢???
}
}
}

LL ans = 0;

for(int i = 1; i <= m; i ++ ) // 计算枚举到n,最后一个数字为i的所有方案数
ans = (ans + f[n][i]) % MOD;

cout << ans << endl;

return 0;
}

为什么要加MOD在模MOD呢???
感谢 Gzm1317 巨巨的解答。
因为在处理前缀和的时候用了s[j] = (s[j - 1] + f[i - 1][j]) % MOD
可能会导致$s[m] < s[b - 1]$的情况出现,于是需要加上一个MOD,防止出现负数的情况。