Codeforces Round 917 (Div. 2)
A. Least Product
题意:
给出整数数组a,现在可以执行任意次数以下操作:任意选择数组a的一个元素 a i a_i ai,若 a i a_i ai>0可以任意替换为[0, a i a_i ai]的一个数,若 a i a_i ai<0则可以任意替换为[ a i a_i ai,0]的一个数。
最小化a数组中所有元素的乘积需要的最小操作次数。
思路:
- 若存在 a i a_i ai=0,则任何操作都无意义,乘积总为0,操作数为0;
- 若负数的个数为奇数,则最终乘积为负,操作无法扩大元素绝对值,所以此时操作也无意义,操作数为0;
- 其他情况最佳操作为选择任意一个数变成0即可,操作数为1。
AC code:
void solve(){
int cnt = 0;
cin >> n;
for(int i = 0; i < n; i ++){
int u; cin >> u;
if(u < 0) cnt ++;
if(u == 0) cnt = -INF;
}
if(cnt < 0 || cnt % 2){
cout << 0 << endl;
return;
}
cout << 1 << endl;
cout << "1 0" << endl;
}
B. Erase First or Second Letter
题意:
给出长度为n的字符串,每次操作可以选择删除当前字符串的第一个字符或第二个字符,求对初始字符串进行任意次数/顺序的操作后,可以生成的非空字符串的数量。
思路:
删除操作只能删除第一或第二个字符,那么可以保留当前字符为前缀一直走到最后,每次需要统计第一次出现的字符来增加遍历到新字符时的新的字符串的数量,第一和第二字符不可能同时保留,保留的过程可以通过不断删除第二字符来保留第一字符,然后再从初始字符串中删除当前第一字符,再重复保留第二字符。
AC code:
void solve(){
map<char, int> mp;
cin >> n >> s;
int cnt = 0, ans = 0;
for(int i = 0; i < n; i ++){
if(!mp[s[i]]){
mp[s[i]] ++;
cnt ++;
}ans += cnt;
}cout << ans << endl;
}
C. Watering an Array
题意:
对长度为n的整数数组a进行d次操作,每次可以选择以下两个操作之一:
- 对数组a的每个前 b i b_i bi个元素+1
- 统计 a j a_j aj=j的数量c,然后就将c加到分数中,然后将序列全部归0,注意初始分数为0
d可能非常大,所以d多个b序列数组的连接, b = [ v 1 , v 2 , … , v k , v 1 , v 2 , … , v k , … ] b = [v_1, v_2, \ldots, v_k, v_1, v_2, \ldots, v_k, \ldots] b=[v1,v2,…,vk,v1,v2,…,vk,…]
思路:
首先,第一种操作是对序列前缀进行递增操作,无法递减,无法精准操作到某一元素,一旦有过一次第二种操作,整个序列会归零,那么再进行第一次操作对分数的贡献最多为1;
所以要最大化第一次使用第二种操作得到的分数,那么剩下的操作次数/2向下取整即为剩余操作所能获得的分数;
因为每次都必须选择一个操作进行,一旦选择了第二种操作,那么之后的操作次数的分数可以直接得到,我们从前往后进行操作,记录每次得到的分数,取最大值即可。
AC code:
void solve(){
cin >> n >> k >> d;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= k; i ++) cin >> b[i];
int cnt = 0, ans = 0;
for(int i = 1; i <= min(2*n, d); i ++){
cnt = 0;
for(int j = 1; j <= n; j ++){
if(a[j] == j) cnt ++;
}
ans = max(ans, cnt + (d - i) / 2) ;
int now = i % k + (i % k == 0) * k;
for(int j = 1; j <= b[now]; j ++)
a[j] ++;
}
cout << ans << endl;
}