美团2024春招第一场笔试
- 小美的平衡矩阵
小美拿到了一个 n ∗ n n * n n∗n 的矩阵,其中每个元素是 0 0 0 或者 1 1 1。
小美好认为一个矩形区域是完美的,当且仅当该区域内 0 0 0 的数量恰好等于 1 1 1 的数量。
现在,小美希望你回答有多少个 i ∗ i i * i i∗i 的完美矩形区域。你需要回复 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n 的所有答案。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 256M,其他语言512M
输入描述:
第一行输入一个正整数
n
n
n ,代表矩阵大小。
接下来的
n
n
n 行,每行输入一个长度为
n
n
n 的
01
01
01 串,用来表示矩阵。
1
≤
n
≤
200
1 \leq n \leq 200
1≤n≤200
输出描述:
输出
n
n
n 行,第
i
i
i 行输出
i
∗
i
i * i
i∗i 的完美矩形区域的数量。
示例1
输入例子:
4
1010
0101
1100
0011
输出例子:
0
7
0
1
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
in.nextLine();
String[] s = new String[n + 1];
for(int i = 1; i <= n; i ++ ){
s[i] = in.nextLine();
}
int[][] a = new int[n + 1][n + 1];
for(int i = 1; i <= n; i ++ ){
for(int j = 1; j <= n; j ++ ){
a[i][j] = s[i].charAt(j - 1) - '0';
a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
}
}
for(int w = 1; w <= n; w ++ ){
if(w % 2 != 0){
System.out.println(0);
continue;
}
int ans = 0, tar = w * w;
for(int i = 1; i <= n; i ++ ){
int x2 = i - 1 + w;
if(x2 > n) break;
for(int j = 1; j <= n; j ++ ){
int y2 = j - 1 + w;
if(y2 > n) continue;
if(getSum(a, i, j, x2, y2) == tar / 2) ans ++;
}
}
System.out.println(ans);
}
in.close();
}
public static int getSum(int[][] a, int x1, int y1, int x2, int y2){
return a[x2][y2] - a[x1 - 1][y2] - a[x2][y1 - 1] + a[x1 - 1][y1 - 1];
}
}
- 小美的数数组询间
小美拿到了一个由正整数组成的数组,但其中有一些元素是未知的(用 0 来表示)。
现在小美想知道,如果那些未知的元素在区间 [l, r] 范围内随机取值的话,数组所有元素之和的最小值和最大值分别是多少?
共有 q 次询问。
输入描述:
第一行输入两个正整数 n, q,代表数组大小和询问次数。
第二行输入 n 个整数 a[i],其中如果输入的 a[i] 为 0,则说明 a[i] 是未知的。
接下来的 q 行,每行输入两个正整数 l, r,代表一次询问。
1 <= n, q <= 10^5
0 <= a[i] <= 10^9
1 <= l <= r <= 10^9
输出描述:
输出 q 行,每行输出两个正整数,代表所有元素之和的最小值和最大值。
示例1
输入例子:
3 2
1 0 3
1 2
4 4
输出例子:
5 6
8 8
例子说明: 只有第二个元素是未知的。
第一次询问,数组最小的和是 1+1+3=5,最大的和是 1+2+3=6。
第二次询问,显然数组的元素必然是 8。
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e5 + 6;
int a[N];
int main() {
int n, q;
cin >> n >> q;
ll ans = 0, cnt = 0;
for(int i = 0; i < n; i ++ ){
cin >> a[i];
ans += a[i];
if(a[i] == 0) cnt ++;
}
while(q -- ){
ll l, r;
cin >> l >> r;
cout << ans + l * cnt << " " << ans + r * cnt << "\n";
}
}
// 64 位输出请用 printf("%lld")
- 小美的MT
MT是美团的缩写,因此小美很喜欢这两个字母。
现在小美拿到一个仅由大写字母组成的字符串,她可以最多操作k次,每次可以修改任意一个字符。小美想知道,操作结束后共有多多少’M’和’T’字符?
输入描述:
第一行输入两个正整数n,k,代表字符串长度和操作次数。
第二行输入一个长度为n的、仅由大写字母组成的字符串。
1≤k≤n≤10^5
输出描述:
输出操作结束后共有多少个’M’和’T’字符。
示例1
输入例子:
5 2
MTUAN
输出例子: 4
修改第三个和第五个字符,形成的字符串为 MTTAM,这样共有 4 个 ‘M’ 和 ‘T’。
#include <iostream>
#include <cstring>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
string s;
cin >> s;
int ans = 0;
for(auto x : s){
if(x == 'M' || x == 'T'){
ans ++;
}else{
if((-- k) >= 0) ans ++;
}
}
cout << ans;
}
// 64 位输出请用 printf("%lld")
- 小美的朋友关系
小美认为,在人际交往中,但是随着时间的流逝,朋友的关系也是会慢慢淡忘的,最终朋友关系就淡忘了。
现在初始有一些朋友关系,存在一些事件会导致两个人淡忘了他们的朋友关系。小美想知道某一时刻中,某两人是否可以通过朋友介绍互相认识?
事件共有2种:
1 uv:代表编号u的人和编号v的人淡忘了他们的朋友关系。
2 uv:代表小美查询编号u的人和编号v的人是否能通过朋友介绍互相认识。
注:介绍可以有多层。比如2号把1号介绍给3号,然后3号再把1号介绍给4号,这样1号和4号就认识了。
输入描述:
第一行输入三个正整数n,m,q,代表总人数,初始的朋友关系数量,发生的事件数量。
接下来的m行,每行输入两个正整数u,v,代表初始编号u的人和编号v的人是朋友关系。
接下来的q行,每行输入三个正整数op,u,v,含义如题目描述所述。
1 ≤ n ≤ 10^9
1 ≤ m, q ≤ 10^5
1 ≤ u, v ≤ n
1 ≤ op ≤ 2
保证至少存在一次查询操作。
输出描述:
对于每次2号操作,输出一行字符串代表查询的答案。如果编号u的人和编号v的人能通过朋友介绍互相认识,则输出"Yes"。否则输出"No"。
示例1
输入例子:
5 3 5
1 2
2 3
4 5
1 1 5
2 1 3
2 1 4
1 1 2
2 1 3
输出例子:
Yes
No
No
例子说明:
第一次事件,1号和5本来就是朋友,所以无事发生。
第二次事件是查询,1号和3号可以通过2号的介绍认识。
第三次事件是查询,显然1号和4号无法互相认识。
第四次事件,1号和2号淡忘了。
第五次事件,此时1号无法再经过2号和3号互相认识了。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 6;
// 离散化
map<int, int> mp;
int idx = 0;
int get(int x){
return mp[x] == 0 ? mp[x] = ++ idx : mp[x];
}
// 未淡化前的朋友集合,淡化后的朋友集合
set<pair<int, int>> alls, thens;
// 并查集
int f[N * 2];
// 结果,操作
bool r[N];
int ops[N][3];
// 查询
int find(int x){
return f[x] == x ? x : f[x] = find(f[x]);
}
// 合并
void merge(int x, int y){
int fx = find(x), fy = find(y);
f[fx] = fy;
}
int main() {
int n, m, q;
cin >> n >> m >> q;
while(m -- ){
int a, b;
cin >> a >> b;
a = get(a), b = get(b);
if(a > b) swap(a, b);
f[a] = a, f[b] = b;
alls.insert({a, b}), thens.insert({a, b});
}
for(int i = 0; i < q; i ++ ){
int a, b, c;
cin >> a >> b >> c;
ops[i][0] = a, b = get(b), c = get(c);
if(b > c) swap(b, c);
ops[i][1] = b, ops[i][2] = c;
f[b] = b, f[c] = c;
if(a == 1) thens.erase({ops[i][1], ops[i][2]});
}
for(auto& [a, b] : thens){
merge(a, b);
}
for(int i = q - 1; i >= 0; i -- ){
auto[a, b, c] = ops[i];
if(a == 2) r[i] = find(b) == find(c);
else{
if(alls.find({b, c}) != alls.end()) merge(b, c);
}
}
for(int i = 0; i < q; i ++ ){
if(ops[i][0] == 2)
r[i] == true ? puts("Yes") : puts("No");
}
}
// 64 位输出请用 printf("%lld")
5.小美的区间删除
小美拿到了一个大小为n的数组,她希望删除一个区间后,使得剩余所有元素的乘积末尾至少有k个0。小美想知道,一共有多少种不同的删除方案?
输入描述:
第二行输入两个正整数n,k。
第三行输入n个正整数a_i,代表小美拿到的数组。
1 ≤ n, k ≤ 10^5
1 ≤ a_i ≤ 10^9
输出描述:
一个整数,代表删除的方案数。
示例1
输入例子:
5 2
2 5 3 4 20
输出例子: 4
第一个方案,删除[3]。
第二个方案,删除[4]。
第三个方案,删除[3,4]。
第四个方案,删除[2]。
#include <iostream>
using namespace std;
const int N = 1e5 + 6;
int two[N], five[N];
long long ans;
int count(int x, int r){
int t = 0;
while(x % r == 0){
t ++;
x /= r;
}
return t;
}
int main() {
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i ++ ){
int x;
cin >> x;
two[i] = two[i - 1] + count(x, 2);
five[i] = five[i - 1] + count(x, 5);
}
int s2 = two[n], s5 = five[n];
for(int l = 1; l <= n; l ++ ){
int r = n;
int t2 = s2 - two[r] + two[l - 1];
int t5 = s5 - five[r] + five[l - 1];
while(l <= r && (t2 < k || t5 < k)){
t2 += two[r] - two[r - 1];
t5 += five[r] - five[r - 1];
r --;
}
ans += r - l + 1;
}
cout << ans;
}
// 64 位输出请用 printf("%lld")