小美的平衡矩阵
题目描述
小美拿到了一个 n × n n \times n n×n 的矩阵,其中每个元素是 0 0 0 或者 1 1 1。小美认为一个矩形区域是完美的,当且仅当该区域内 0 0 0 的数量恰好等于 1 1 1 的数量。现在,小美希望你回答有多少个 i × i i \times i i×i 的完美矩形区域。你需要回答 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n 的所有答案。
输入描述
第一行输入一个正整数
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 \times i i×i 的完美矩形区域的数量。
解题思路
通过二维前缀和可快速得到一个矩形区域内的 0 0 0 或 1 1 1 的个数。
令 d p [ i ] [ j ] dp[i][j] dp[i][j] = 以 ( 1 , 1 ) (1,1) (1,1) 为左上角, ( i , j ) (i,j) (i,j) 为右下角的矩形中 1 1 1 的个数,则有如下递推式:
- d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] − d p [ i − 1 ] [ j − 1 ] dp[i][j] = dp[i-1][j] + dp[i][j-1] -dp[i-1][j-1] dp[i][j]=dp[i−1][j]+dp[i][j−1]−dp[i−1][j−1]
对于一个以 ( i , j ) (i,j) (i,j) 为左上角、长为 k k k 的矩形,其中 1 1 1 的个数为:
- d p [ i + k ] [ j + k ] − d p [ i + k ] [ j − 1 ] − d p [ i − 1 ] [ j + k ] + d p [ i − 1 ] [ j − 1 ] dp[i + k][j + k] - dp[i + k][j - 1] - dp[i - 1][j + k] + dp[i - 1][j - 1] dp[i+k][j+k]−dp[i+k][j−1]−dp[i−1][j+k]+dp[i−1][j−1]
代码实现
const int n = 2e2 + 5;
int dp[n][n];
char s[n];
int main() {
int n;
scanf("%d", &n);
// 输入、计算二维前缀和
for (int i = 1; i <= n; i++) {
scanf("%s", s + 1);
for (int j = 1; j <= n; j++)
dp[i][j] = dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1] + (s[j] == '1');
}
// mp[i] = 大小为 i*i 的矩形的数量
unordered_map<int, int> mp;
// 枚举每个大小为 i*i 的矩形
for (int i = 1, t; i <= n; i++) {
for (int j = 1; j <= n; j++) {
for (int k = 1; i + k <= n && j + k <= n; k += 2) {
t = dp[i + k][j + k] - dp[i + k][j - 1] - dp[i - 1][j + k] + dp[i - 1][j - 1];
if (t * 2 == (k + 1) * (k + 1))mp[k + 1]++;
}
}
}
// 输出每个答案
for (int i = 1; i <= n; i++)
printf("%d\n", mp[i]);
return 0;
}
时间复杂度: O ( n 3 ) O(n^3) O(n3)。
空间复杂度: O ( n 2 ) O(n^2) O(n2)。
小美的数组询问
题目描述
小美拿到了一个由正整数组成的数组,但其中有一些元素是未知的(用 0 0 0 来表示)。现在小美想知道,如果那些未知的元素在区间 [ l , r ] [l,r] [l,r] 范围内随机取值的话,数组所有元素之和的最小值和最大值分别是多少?共有 q q q 次询问。
输入描述
第一行输入两个正整数 n , q n,q n,q,代表数组大小和询问次数。
第二行输入 n n n 个整数 a i a_i ai ,其中如果输入的 a i a_i ai 为 0 0 0,那么说明 a i a_i ai 是未知的。
接下来的 q q q 行,每行输入两个正整数 l , r l,r l,r,代表一次询问。
- 1 ≤ n , q ≤ 1 0 5 1\leq n,q \leq 10^5 1≤n,q≤105
- 0 ≤ a i ≤ 1 0 9 0 \leq a_i \leq 10^9 0≤ai≤109
- 1 ≤ l ≤ r ≤ 1 0 9 1\leq l \leq r \leq 10^9 1≤l≤r≤109
输出描述
输出 q q q 行,每行输出两个正整数,代表所有元素之和的最小值和最大值。
解题思路
选 l l l 得到最小值,选 r r r 得到最大值。
代码实现
typedef long long ll;
int main() {
int n, q, cnt = 0;
ll x, l, r, sum = 0;
scanf("%d%d", &n, &q);
while (n--) {
scanf("%lld", &x);
x == 0 ? cnt++ : sum += x;
}
while (q--) {
scanf("%lld%lld", &l, &r);
printf("%lld %lld\n", sum + l * cnt, sum + r * cnt);
}
return 0;
}
时间复杂度: O ( n ) O(n) O(n)。
空间复杂度: O ( 1 ) O(1) O(1)。
小美的 MT
题目描述
M T MT MT 是美团的缩写,因此小美很喜欢这两个字母。现在小美拿到了一个仅由大写字母组成字符串,她可以最多操作 k k k 次,每次可以修改任意一个字符。小美想知道,操作结束后最多共有多少个 M M M 和 T T T 字符?
输入描述
第一行输入两个正整数 n , k n,k n,k,代表字符串长度和操作次数。
第二行输入一个长度为 n n n 的、仅由大写字母组成的字符串。
- 1 ≤ k ≤ n ≤ 1 0 5 1\leq k \leq n \leq 10^5 1≤k≤n≤105
输出描述
输出操作结束后最多共有多少个 M M M 和 T T T 字符。
解题思路
尽可能修改每个不是 M M M 或 T T T 的字符。
代码实现
int main() {
int n, k, cnt = 0;
char c;
scanf("%d%d\n", &n, &k);
while (n--) {
scanf("%c", &c);
if (c == 'M' || c == 'T')cnt++;
else if (k > 0)cnt++, k--;
}
printf("%d", cnt);
return 0;
}
时间复杂度: O ( n ) O(n) O(n)。
空间复杂度: O ( 1 ) O(1) O(1)。
小美的朋友关系
题目描述
小美认为,在人际交往中,但是随着时间的流逝,朋友的关系也是会慢慢变淡的,最终朋友关系就淡忘了。
现在初始有一些朋友关系,存在一些事件会导致两个人淡忘了他们的朋友关系。小美想知道某一时刻中,某两人是否可以通过朋友介绍互相认识?
事件共有 2 2 2 种:
- 1 u v 1\ u\ v 1 u v:代表编号 u u u 的人和编号 v v v 的人淡忘了他们的朋友关系。
- 2 u v 2\ u\ v 2 u v:代表小美查询编号 u u u 的人和编号 v v v 的人是否能通过朋友介绍互相认识。
输入描述
第一行输入三个正整数 n , m , q n,m,q n,m,q,代表总人数,初始的朋友关系数量,发生的事件数量。
接下来的 m m m 行,每行输入两个正整数 u , v u,v u,v,代表初始编号 u u u 的人和编号 v v v 的人是朋友关系。
接下来的 q q q 行,每行输入三个正整数 o p , u , v op,u,v op,u,v,含义如题目描述所述。
- 1 ≤ n ≤ 1 0 9 1\leq n \leq 10^9 1≤n≤109
- 1 ≤ m , q ≤ 1 0 5 1\leq m,q \leq 10^5 1≤m,q≤105
- 1 ≤ u , v ≤ n 1\leq u,v \leq n 1≤u,v≤n
- 1 ≤ o p ≤ 2 1\leq op \leq 2 1≤op≤2
保证至少存在一次查询操作。
输出描述
对于每次 2 2 2 号操作,输出一行字符串代表查询的答案。如果编号 u u u 的人和编号 v v v 的人能通过朋友介绍互相认识,则输出"Yes"。否则输出"no"。
解题思路
- n n n 的规模超过 1 0 6 10^6 106,需要离散化。
- 通过并查集可快速判断两人是否为朋友关系。
- 并查集不方便删除,故采用反向并查集。对于每个淡忘操作 ( u , v ) (u,v) (u,v),若初始存在 关系 ( u , v ) (u,v) (u,v),则此操作前均存在关系 ( u , v ) (u,v) (u,v),则此操作后均不存在关系 ( u , v ) (u,v) (u,v)。因此,反向顺序进行操作,将删除转为添加,将结果正向输出即为答案。
代码实现
const int n = 1e5 + 5;
// 并查集
int p[n];
// 初始关系、关系记录
set<pair<int, int>> irs, rs;
// 操作
int ops[n][3];
// 离散化
int idx = 0;
unordered_map<int, int> mp;
/**
* 将编号x转换为离散化后的值
*/
int dsz(int x) {
if (mp[x] == 0)mp[x] = ++idx;
return mp[x];
}
/**
* 找到节点x所在集合的根节点
*/
int find(int x) {
if (p[x] != x)p[x] = find(p[x]);
return p[x];
}
/**
* 合并节点u和节点v所在两个集合
*/
void unite(int u, int v) {
int pu = find(u), pv = find(v);
if (pu != pv)p[pu] = pv;
}
int main() {
int n, m, q, op, u, v;
scanf("%d%d%d", &n, &m, &q);
iota(p, p + n, 0);
// 关系记录 = 初始关系
while (m--) {
scanf("%d%d", &u, &v);
u = dsz(u), v = dsz(v);
if (u > v)swap(u, v);
irs.insert({u, v});
rs.insert({u, v});
}
// 记录操作、关系记录 = 初始关系 - 淡忘关系
for (int i = 0; i < q; i++) {
scanf("%d%d%d", &op, &u, &v);
u = dsz(u), v = dsz(v);
if (u > v)swap(u, v);
ops[i][0] = op, ops[i][1] = u, ops[i][2] = v;
if (op == 1)rs.erase({u, v});
}
// 根据关系记录建立并查集
for (auto &[a, b]: rs) unite(a, b);
// 从前往后是删除关系,从后往前是添加关系
bool r[q + 1];
memset(r, 0, sizeof r);
for (int i = q - 1; i >= 0; i--) {
if (ops[i][0] == 2)r[i] = find(ops[i][1]) == find(ops[i][2]);
// 初始为朋友关系的两人才能淡化朋友关系
else if (irs.find({ops[i][1], ops[i][2]}) != irs.end())unite(ops[i][1], ops[i][2]);
}
// 输出结果
for (int i = 0; i < q; i++) {
if (ops[i][0] == 2)printf("%s\n", r[i] ? "Yes" : "no");
}
return 0;
}
时间复杂度: O ( m + q ) O(m+q) O(m+q)。
空间复杂度: O ( m + q ) O(m+q) O(m+q)。
小美的区间删除
题目描述
小美拿到了一个大小为 n n n 的数组,她希望删除一个区间后,使得剩余所有元素的乘积末尾至少有 k k k 个 0 0 0。小美想知道,一共有多少种不同的删除方案?
输入描述
第一行输入两个正整数 n , k n,k n,k。
第二行输入 n n n 个正整数 a i a_i ai,代表小美拿到的数组。
- 1 ≤ n , k ≤ 1 0 5 1\leq n,k \leq 10^5 1≤n,k≤105
- 1 ≤ a i ≤ 1 0 9 1\leq a_i \leq 10^9 1≤ai≤109
输出描述
一个整数,代表删除的方案数。
解题思路
一个 2 2 2 和 一个 5 5 5 相乘可得到一个 0 0 0。
代码实现
const int n = 1e5 + 5;
// s2[i] = a[1..i]中包含2的个数
// s5[i] = a[1..i]中包含5的个数
int s2[n], s5[n];
/**
* 统计 x 中包含的 2 和 5 的个数
*/
pair<int, int> count25(int x) {
int c2 = 0, c5 = 0;
while (!(x & 1))c2++, x /= 2;
while (x % 5 == 0)c5++, x /= 5;
return {c2, c5};
}
int main() {
int n, k, x;
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
auto [c2, c5] = count25(x);
s2[i] = c2 + s2[i - 1], s5[i] = c5 + s5[i - 1];
}
int l, r;
// 检查 l 和 r 是否不符合要求
auto check = [&] {
return s2[r] - s2[l - 1] > s2[n] - k || s5[r] - s5[l - 1] > s5[n] - k;
};
long long res = 0;
// 枚举每类以 l 为左边界的区间
for (l = 1; l <= n; l++) {
// 找到以 l 为左边界且满足题目条件的最大区间
int i = l, j = n;
while (i < j) {
r = (i + j + 1) >> 1;
if (check())j = r - 1;
else i = r;
}
r = i;
// 若[l,r] 满足条件,则 [l,r-1] 也满足条件,类推
if (!check())res += r - l + 1;
}
printf("%lld", res);
return 0;
}
时间复杂度: O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))。
空间复杂度: O ( n ) O(n) O(n)。
END
文章文档:公众号 字节幺零二四
回复关键字可获取本文文档。
题目来源:美团2024年春招第一场笔试【技术】
文章声明:题目来源 牛客 平台,如有侵权,请联系删除!