A.Least Product(思维)
题意:
给出一个数组 a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1,a2,...,an,你可以进行若干次以下操作:
- 选择数组中的一个元素 a i a_i ai,将这个数字修改为 0 ∼ a i 0 \sim a_i 0∼ai之间的任意数字。
问,最少需要多少次操作可以使得 ∏ i = 1 n a i \prod\limits_{i = 1}^{n}a_i i=1∏nai的结果最小,并输出对应的操作。
分析:
可以将问题分成两种情况:
-
存在 a i = 0 a_i = 0 ai=0或负数的出现次数为奇数,此时无论进行任何操作都无法使结果变得更小,输出0
-
其他情况,由于数字只能被修改到 0 ∼ a i 0 \sim a_i 0∼ai之间,那么正负数是无法互相转换的,因此,当结果为正数时,任选一个 a i a_i ai,将其修改为 0 0 0,就能获得最小的结果
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 3e5 + 5e2;
void solve() {
int n;
cin >> n;
int zero = 0, sign = 0;
for (int i = 1; i <= n; i++) {
int a;
cin >> a;
if (a == 0) {
zero = 1;
} else if (a < 0) {
sign++;
}
}
if (zero || sign % 2 == 1) cout << 0 << endl;
else {
cout << 1 << endl;
cout << "1 0" << endl;
}
}
int main() {
int Case;
cin >> Case;
while (Case--) {
solve();
}
return 0;
}
B.Erase First or Second Letter(思维)
题意:
给出一个长度为 n n n的字符串 s s s,你可以进行若干次以下操作:
-
移除第一个字符
-
移除第二个字符
问:经过若干次操作后,可以获得多少种不同的非空字符串?
分析:
将字符串划分为前后两部分,枚举两部分的分界点,每次枚举完分界点后,将后半部分视为不进行修改的部分,仅会对前半部分进行操作,那么由于每次枚举的后半部分均不同,为了保证方案的独立性,前半部分仅保留一个字符,那么此时的方案数就是前半部分中包含的不同字母的数量。
使用计数数组或set
动态记录不同字符数量,每次枚举分界点后更新前半部分字符种类并记录到答案中即可。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 3e5 + 5e2;
int vis[30];
void solve() {
memset(vis, 0, sizeof (vis));
int n;
string s;
cin >> n >> s;
int cnt = 0, ans = 0;
for (int i = 0; i < n; i++) {
vis[s[i] - 'a']++;
if (vis[s[i] - 'a'] == 1) cnt++;
ans += cnt;
}
cout << ans << endl;
}
int main() {
int Case;
cin >> Case;
while (Case--) {
solve();
}
return 0;
}
C.Watering an Array(枚举)
题意:
给出一个长度为 n n n的数组 a a a,你将在接下来的 d d d天里每天选择执行以下两个操作之一:
-
给所有的 a 1 , a 2 , . . . , a b i a_1, a_2, ..., a_{b_i} a1,a2,...,abi加上一,其中 b i b_i bi为题目给出的第 i i i天的操作数。
-
数组 a a a中 a i = i a_i = i ai=i的数量就是你本轮获得的得分,然后将 a a a数组所有元素修改为 0 0 0
问:最多可以获得多少得分?
Tips:由于天数较多,因此 b b b数组采用 b = [ v 1 , v 2 , . . . , v k , v 1 , v 2 , . . . , v k , . . . ] b = [v_1, v_2, ..., v_k, v_1, v_2, ..., v_k, ...] b=[v1,v2,...,vk,v1,v2,...,vk,...]的形式给出。
分析:
如果数组 a a a开始全部为 0 0 0,那么最优策略就是操作 1 , 2 1,2 1,2轮流进行,可以获得 ⌊ d 2 ⌋ \lfloor\frac{d}{2}\rfloor ⌊2d⌋点得分(注意:开始时若 a 1 ≥ 1 a_1 \ge 1 a1≥1,则需要使用一次操作 2 2 2将 a 1 a_1 a1修改为 0 0 0,此时得分为 ( ⌊ d − 1 2 ⌋ + 开始时数组元素可以产生的得分 (\lfloor\frac{d - 1}{2}\rfloor + \text{开始时数组元素可以产生的得分} (⌊2d−1⌋+开始时数组元素可以产生的得分)。
而由于初始的数组是包含初始数字的,且长度为 n n n的数组最多可以产生 n n n点得分,因此,只需要在 2 × n 2 \times n 2×n的操作次数内均有可能超过循环操作获取的 n n n点固定得分。
枚举第一次进行操作 2 2 2前会进行多少次操作 1 1 1,每次遍历数组统计得分,剩余的操作次数使用最优策略进行。记录过程中最大的得分即可。
坑点
既然获取的长度为 n n n的数字最高可以产生 n n n点得分,那么只枚举 n n n次操作是否就够了?
反例:
原数组为 2 , 1 , 2 , . . . , n − 1 2, 1, 2, ..., n - 1 2,1,2,...,n−1,给出的 b b b数组为 [ 1 , 1 , . . . , 1 , n ] [1, 1, ..., 1, n] [1,1,...,1,n],其中 b i = n b_i = n bi=n前有 ( 2 × n ) − 4 (2 \times n) - 4 (2×n)−4个1,此时 d = 2 × n d = 2 \times n d=2×n。
可以发现,枚举 0 ∼ n 0 \sim n 0∼n次操作 1 1 1,均无法产生额外得分,此时获得的最高总分为 ⌊ d − 1 2 ⌋ = n − 1 \lfloor \frac{d - 1}{2} \rfloor = n - 1 ⌊2d−1⌋=n−1,而依次使用 ( 2 × n ) − 3 (2 \times n) - 3 (2×n)−3次操作 1 1 1,再进行操作 2 2 2,可以获得 n − 1 n - 1 n−1点得分,此时还剩下两次操作,还可以继续一次最优策略,最后得分为 n n n。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 3e5 + 5e2;
LL a[MAXN], b[MAXN];
void solve() {
LL n, k, d;
cin >> n >> k >> d;
LL ans = 0;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 0; i < k; i++) cin >> b[i];
for (int i = 0; i <= 2 * n && i < d; i++) {
LL cnt = 0;
for (int j = 1; j <= n; j++) if (a[j] == j) cnt++;
ans = max(ans, cnt + (d - i - 1) / 2);
for (int j = 1; j <= b[i % k]; j++) {
if (a[j] == j) cnt--;
a[j]++;
if (a[j] == j) cnt++;
}
}
cout << ans << endl;
}
int main() {
int Case;
cin >> Case;
while (Case--) {
solve();
}
return 0;
}
学习交流
以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。