Codeforces Round #780 (Div. 3)
A - Vasya and Coins
题意:有a个1块的硬币,b个2块的硬币,问最小不能用上述两种硬币表示的数量是多少?
如果有大于等于1块价值为1的硬币,那么a1 + b2 = Max 范围内所有数字都可以表示,ans = MAX + 1
如果没有1块的价值为1的硬币,答案就是1,因为没有价值为1的硬币,所以无法表示
点击查看代码
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define LL long long
using namespace std;
int main() {
int t;
int n, m;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
if (n >= 1) {
printf("%d\n", n + m*2 + 1);
} else {
printf("%d\n", 1);
}
}
return 0;
}
B - Vlad and Candies
题意:有n个种糖果,每种糖果有a[i]个,每次会挑糖果数量最多的吃,并且要保证,上传吃的糖果和这次吃的糖果类型不一样,给出这样n个糖果对应糖果的数量a[i],问能不能满足上述条件。
最多的糖果和次多的糖果数量之差不能超过1,不然就连续吃两次最多类型的糖果。
点击查看代码
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define LL long long
using namespace std;
const int maxx = 2e5+6;
int a[maxx];
int main() {
int t;
int n, m;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
int first = 0;
int second = 0;
for (int i=1; i<=n; i++) {
scanf("%d", &a[i]);
if (a[i] > first) {
second = first;
first = a[i];
} else if (a[i] > second) {
second = a[i];
}
}
if (n == 1 && first == 1) {
printf("YES\n");
} else if (n > 1 && first - second <= 1) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
C. Get an Even String
题意:给出一个小写字母组成的字符串,能不能把这个字符串去掉几个字符,使得字符串的a[i] = a[i+1] 其中 i % 2 ==1,也就是说能组成字符串,是有两两相同的字符组成而成,即AABBCC型,反过来看,只需要找出最长的AABBCC型组成的字符串即可。
可以发现一个规律,需要不断找到最近的相邻的两两成对的字符组成对,并且字符个数需要是偶数,那么贪心的从前往后找,如果找到一个字符出现两次之后,就清空两个相同字符之间的所有字符。之间的字符没有用,是因为如果之间的字符和其他字符够成了相同字符串,那么删除的长度是小于第一次找到相关字符出现两次所需要删除的长度。
点击查看代码
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <queue>
#define LL long long
using namespace std;
const int maxx = 2e5+6;
vector<int>p[28];
int ans[maxx];
int main() {
int t;
int n, m;
string str;
scanf("%d", &t);
while (t--) {
cin>>str;
map<int,int> p;
int len = str.length();
int cnt = 0;
for (int i=0; i<=len; i++) {
int wordValue = str[i] - 'a';
p[wordValue]++;
if (p[wordValue] == 2) {
p.clear();
cnt++;
}
}
printf("%d\n", len - cnt*2);
}
return 0;
}
D - Maximum Product Strikes Back
要求的其实就是对于一个array里面,某一段连续乘积的最大值。看数据范围,可以发现a[i]的取值其实只有-2, -1, 0, 1, 2,并且i的范围却到了2e5的范围,假如连续64个2,数据就不太好统计了,看起来不能用简单取乘积来算
可以发现,答案的大小只和2,-2的个数有关,-1,1对答案没有影响,0还会劣化答案对取值,因此假如要选择一段取最大值容易有以下结论:
- 中间不能有0 ——> 答案被0所分隔
- 尽量取多的2 这里的2可能是 a[i] = 2 或者 -2 * -2
- 继续思考发现,对于被0分隔的每一段 l r 这一段内无0,那么影响答案只有正负号
分类讨论—讨论被0分隔的区间L,R
- 里面有偶数个负数,那么这个区间的2和-2的个数就是最大值
- 假如里面有奇数个负数,答案就不太好判断了,肯定是要去掉一个负数的,但是去掉哪个呢?
容易想到一个结论,肯定是去掉第一个负数或者最后一个负数,那么区间其实变成了 0...L...negativeL...negativeR....R...0
- 去掉negetiveL 有两种做法 [L,negativeL - 1] [negativeL + 1, R]
- 去掉negetiveR 有两个种做法, [L,negativeR - 1] [negativeR + 1, R]
那么其实只需要统计上述四个区间内2、-2的个数,最后维护一个2、-2个数的最大值即可。
点击查看代码
int a[maxx];
int pre[5][maxx];
int n, m;
int maxCount = 0, ans_l = 1,ans_r = n;
void updateMax(int count, int l, int r) {
if (maxCount < count && l <= r) {
maxCount = count;
ans_l = l;
ans_r = r;
}
}
int main() {
int t;
string str;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
ans_l = 1;
ans_r = n;
maxCount = 0;
memset(pre, 0, sizeof(pre));
for (int i=1; i<=n; i++) {
scanf("%d", &a[i]);
}
for (int i=1; i<=n; i++) {
int value = a[i] + 2;
pre[0][i] = pre[0][i-1];
pre[1][i] = pre[1][i-1];
pre[2][i] = pre[2][i-1];
pre[3][i] = pre[3][i-1];
pre[4][i] = pre[4][i-1];
pre[value][i]++;
}
int zeroL = 0, zeroR = 0, negetiveFrist = 0, negetiveEnd = 0, negetiveCount = 0;
for (int i=1; i<=n; i++) {
if (a[i] != 0) {
if (zeroL == 0) {
zeroL = i;
}
zeroR = i;
if (a[i] < 0) {
if (negetiveFrist == 0) {
negetiveFrist = i;
}
negetiveEnd = i;
negetiveCount++;
}
}
if (a[i] == 0 || i == n) {
if (zeroL == 0) continue;
if (negetiveCount % 2) {
int towCountLL = pre[4][negetiveFrist - 1] - pre[4][zeroL - 1] + pre[0][negetiveFrist - 1] - pre[0][zeroL - 1];
updateMax(towCountLL, zeroL, negetiveFrist - 1);
int towCountLR = pre[4][zeroR] - pre[4][negetiveFrist] + pre[0][zeroR] - pre[0][negetiveFrist];
updateMax(towCountLR, negetiveFrist + 1, zeroR);
int towCountRL = pre[4][negetiveEnd - 1] - pre[4][zeroL - 1] + pre[0][negetiveEnd - 1] - pre[0][zeroL - 1];
updateMax(towCountRL, zeroL, negetiveEnd - 1);
int towCountRR = pre[4][zeroR] - pre[4][negetiveEnd] + pre[0][zeroR] - pre[0][negetiveEnd];
updateMax(towCountRR, negetiveEnd + 1, zeroR);
} else {
int countTwo = pre[4][zeroR] - pre[4][zeroL - 1] + pre[0][zeroR] - pre[0][zeroL - 1];
updateMax(countTwo, zeroL, zeroR);
}
zeroL = 0;
zeroR = 0;
negetiveCount = 0;
negetiveFrist = 0;
negetiveEnd = 0;
}
}
if (maxCount == 0) {
printf("%d %d\n", n, 0);
} else {
printf("%d %d\n", ans_l-1, n - ans_r);
}
}
return 0;
}
E. Matrix and Shifts
尽可能的让多的1放在对角线上,可以发现在对角线的一组数是固定有规律的,对于a[i][0]位置来说和它一组的a[(i+j)%n][j]其中j<=n。
点击查看代码
#include <string.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <set>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <queue>
#define LL long long
using namespace std;
const int maxx = 2e5+6;
char str[2005][2005];
int main() {
int t;
scanf("%d", &t);
while (t--) {
int n;
scanf("%d", &n);
for (int i=0; i<n; i++) {
scanf("%s", str[i]);
}
int countMax = 0;
int maxOne = 0;
int maxZero = 0;
for (int i=0; i<n; i++) {
int count = 0;
for (int j=0; j<n; j++) {
if (str[(i+j)%n][j] == '1') {
maxOne++;
count++;
} else {
maxZero++;
}
}
countMax = max(countMax, count);
}
int changeZero = n - countMax;
int changeOne = maxOne - countMax;
printf("%d\n", changeOne + changeZero);
}
return 0;
}
F2 - Promising String (hard version)
给定一个"-"、"+"组成的字符串,可以用两个"-"替换成一个'"+",给定字符串中有多少子串满足内部+号数量和-号数量相同(两个-可以替换成一个"+")
假如一个字符串里面有a个“-”,有b“+”。那么可以有以下等式
a - 2*k = b + k
也就是即
a - b = 0 (mod 3)
可以把"-"看成是+1,把"+"看成是-1,上述式子就转化成为前缀和。那么答案其实就变成了,对于位置i来说,前缀和为pre[i],需要在[1, i-1]找到有多少个位置j,使得pre[i] - pre[j-1] = 0 (mod 3) 。可以很容易发现,mod 3 只有三种结果 0、1、2,维护这三种前缀和即可,对于pre[i]来说,从pre[i]%3树状数组中,找比自己小的前缀和个数即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define LL long long
using namespace std;
const int MAX = 1e6+10;
int pre[MAX];
int maxx;
LL three[3][MAX];
int lowbit(int x) {
return x&(-x);
}
void add(int x, LL weight, LL c[]) {
while (x <= maxx)
{
c[x] += weight;
x += lowbit(x);
}
}
LL getSum(int x, LL c[]) {
LL sum = 0;
while (x != 0) {
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
string str;
int n;
scanf("%d", &n);
cin>>str;
int len = str.length();
int minn = 0;
for (int i=1; i<=len; i++) {
pre[i] = pre[i-1];
if (str[i-1] == '-') {
pre[i]++;
} else {
pre[i]--;
}
minn = min(pre[i], minn);
}
for (int i = 0; i <= len; i++) {
pre[i] += (-minn + 1);
maxx = max(pre[i], maxx);
}
for (int i=0; i<=maxx; i++) {
three[0][i] = 0;
three[1][i] = 0;
three[2][i] = 0;
}
LL ans = 0;
for (int i=0; i<=len; i++) {
int value = pre[i] % 3;
ans += getSum(pre[i], three[value]);
add(pre[i], 1, three[value]);
}
printf("%lld\n", ans);
}
return 0;
}