米哈游0319笔试总结

写在前面:

题目信息获取来源https://www.nowcoder.com/discuss/467108267630534656,仅交流学习,如有侵权或涉密请联系我删除。

题目 1

题目内容

米小游拿到了一个矩阵,矩阵上有一格有一个颜色,为红色( R )。绿色( G )和蓝色( B )这三种颜色的一种。

然而米小游是蓝绿色盲,她无法分游蓝色和绿色,所以在米小游眼里看来,这个矩阵只有两种颜色,因为蓝色和绿色在她眼里是一种颜色。

米小游会把相同颜色的部分看成是一个连通块。请注意,这里的连通划是上下左右四连通的。

由于色盲的原因,米小游自己看到的连通块数量可能比真实的连通块数量少。

你可以帮米小游计算连通块少了多少吗?

输入描述

第一行输入两个正整数 n 和 m ,代表矩阵的行数和列数。

接下来的 n 行,每行输入一个长度为 m 的,仅包含 R 、G 、B 三种颜色的字符串,代表米小游拿到的矩阵。

\(1 \le n, m \le 1000\)

输出描述

一个整数,代表米小游视角里比真实情况少的连通块数量。

样例

输入

1
2
3
2 6
RRGGBB
RGBGRR

输出

1
3

解题方法

模拟题,只需要进行两遍 DFS 即可。

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
#include<bits/stdc++.h>

using namespace std;

int main() {
const int offset[4][2] = {{1, 0}, {-1, 0}, {0, -1}, {0, 1}};
int n, m;
cin>>n>>m;
vector<vector<int>> g(n, vector<int>(m, 0x3f3f3f3f));
vector<vector<bool>> visit(n, vector<bool>(m, false));
for (int i = 0; i < n; i++) {
string s;
cin>>s;
for (int j = 0; j < m; j++) {
if (s[j] == 'R') {
g[i][j] = 0;
}
else if (s[j] == 'G') {
g[i][j] = 1;
}
else {
g[i][j] = 2;
}
}
}
function<void(int, int, bool)> dfs = [&](int x, int y, bool isreal) {
visit[x][y] = true;
for (int i = 0; i < 4; i++) {
int nx = x + offset[i][0], ny = y + offset[i][1];
if (nx < 0 || nx >= n || ny < 0 || ny >= m || visit[nx][ny]) {
continue;
}
if (isreal && g[nx][ny] != g[x][y] ||
!isreal && (g[nx][ny] == 0 && g[x][y] != 0 || g[nx][ny] != 0 && g[x][y] == 0)) {
continue;
}
dfs(nx, ny, isreal);
}
};
int real_ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (visit[i][j]) {
continue;
}
real_ans++;
dfs(i, j, true);
}
}
for (int i = 0; i < n; i++) {
fill(visit[i].begin(), visit[i].end(), false);
}
int fake_ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (visit[i][j]) {
continue;
}
fake_ans++;
dfs(i, j, false);
}
}
cout << real_ans - fake_ans << endl;
return 0;
}

题目 2

题目内容

米小游拿到了一个字符串 s 。她可以进行任意次以下两种操作:

删除 s 的一个 "mhy" 子序列。 添加一个 "mhy" 子序列在 s 上。 例如,给定 s 为 "mhbdy" ,米小游进行一次操作后可以使 s 变成 "bd" ,或者变成 "mhmbhdyy" 。

米小游想知道,经过若干次操作后 s 是否可以变成 t ?

注:子序列在原串中的顺序也是从左到右,但可以不连续。

输入描述

第一行输入一个正整数 q ,代表询问的次数。

接下来每两行为一次询问:每行均为一个字符串,分别代表 s 和 t 。

\(1 \le q \le {10^3}\)

字符串的长度均不超过 \({10^3}\)

输出描述

输出 q 行,每行输入一行答案。若可以使 s 变成 t ,则输出 "Yes" 。否则输出 "No" 。

样例

输入

1
2
3
4
5
6
7
3
mhbdy
bd
mhbdy
mhmbhdyy
mhy
abc

输出

1
2
3
Yes
Yes
No

解题方法

yhm->mhyhmy->mhyhmy->hym

通过上例可以看出插入的序列顺序无关,可互相转换,因此只需要统计 hmy 的字母个数和剩余序列即可

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
#include<bits/stdc++.h>

using namespace std;

int main() {
int t;
cin>>t;
while (t--) {
string s, target;
cin>>s>>target;
vector<int> s_cnts(3, 0), target_cnts(3, 0);
string s_res = "", target_res = "";
for (auto &ch : s) {
if (ch == 'm') {
s_cnts[0]++;
}
else if (ch == 'h') {
s_cnts[1]++;
}
else if (ch == 'y') {
s_cnts[2]++;
}
else {
s_res.push_back(ch);
}
}
for (auto &ch : target) {
if (ch == 'm') {
target_cnts[0]++;
}
else if (ch == 'h') {
target_cnts[1]++;
}
else if (ch == 'y') {
target_cnts[2]++;
}
else {
target_res.push_back(ch);
}
}
if (s_res == target_res) {
if (s_cnts[0] - target_cnts[0] == s_cnts[1] - target_cnts[1] &&
s_cnts[1] - target_cnts[1] == s_cnts[2] - target_cnts[2]) {
cout << "Yes" << endl;
}
else {
cout << "No" << endl;
}
}
else {
cout << "No" << endl;
}
}
}

题目 3

题目内容

米小游拿到了一个集合(集合中元素互不相等)。

她想知道,该集合有多少个元素数量大于 1 的子集,满足子集内的元素两两之间互为倍数关系?

由于数量可能过大,请对 1e9 + 7 取模。

输入描述

第一行输入一个正整数 n ,代表集合大小。

第二行输入 n 个正整数 \(a_i\),代表集合的元素。

\(1 \le n \le {10}^5\)

\(1 \le a_i \le {10}^6\)

输出描述

一个整数,代表满足题意的子集数量对 对 1e9 + 7 取模的值。

样例

输入

1
2
5
1 2 3 4 5

输出

1
6

解题方法

之前在Leetcode上遇到过类似的题目368. 最大整除子集,所以最开始想到一种时间复杂度为 \(O(n^2)\) 的 DP 做法,其中 \(dp[i]\) 表示以第 i 个元素结尾的整除子集元素数量,对于每一个这样的集合,其包含的合法子集个数为 \({2}^{dp[i] - 1} - 1\),借助快速幂可以求出答案。

但是本题的数据范围要求 \(O(n^2)\) 以下,所以该做法只通过了 20% 的弱数据。本题的正解也是 DP 的做法,不过 \(dp[i]\) 表示包含第 i 个元素的合法子集个数,可以通过枚举第 i 个元素的所有因子进行状态转移,时间复杂度可以优化到 \(O(n\sqrt{n})\) ,代码如下:

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
#include<bits/stdc++.h>

using namespace std;

int main() {
const int mod = 1e9 + 7;
const int maxn = 1e6 + 5;
int n;
cin>>n;
vector<int> nums(n);
vector<int> pos(maxn, -1);
for (int i = 0; i < n; i++) {
cin>>nums[i];
}
sort(nums.begin(), nums.end());
for (int i = 0; i < n; i++) {
pos[nums[i]] = i;
}
vector<int> dp(n, 0);
long long int ans = 0;
for (int i = 0; i < n; i++) {
int cur = nums[i];
for (int j = 1; j * j <= cur; j++) {
if (cur % j == 0) {
if (pos[cur/j] != -1 && cur/j != j) { // 保证 nums[i] 的平方根不会统计两次
dp[i] += dp[pos[cur/j]];
}
if (pos[j] != -1) {
dp[i] += dp[pos[j]];
}
}
}
ans = (ans + dp[i])%mod;
dp[i]++; // 加入 {nums[i]} 这种情况,为后面的元素做转移
}
cout << ans << endl;
}

米哈游0319笔试总结
http://shijieq.github.io/2023/03/19/Algorithm/InternJob/Mihoyo0319/
Author
ShijieQ
Posted on
March 19, 2023
Licensed under