Codeforces Round #780 (Div. 3)

Codeforces Round #780 (Div. 3) solution

A - Vasya and Coins

题意

\(a\)\(1\) , \(b\)\(2\)。求出最小不能表示的正整数。
数据范围: \(0 \leq a,b \leq 10^8\)

题解

  • 如果 \(a = 0\),答案显然为 \(1\)
  • 如果 \(a \geq 1\),答案为\(2b + a + 1\)

证明如下:

\(x = 2 \times x + 1\),这样可以表示\([1, 2b + 1]\)的所有整数。

\(x = 2\times b + 1 \times y\),这样可以表示\([2b + 2, 2b + a]\)的所有整数。

程序时间复杂度为 \(O(1)\)

C++ 代码示例

# include <bits/stdc++.h>
# define int long long
using namespace std;
signed main() {
	int T; cin >> T;
	while (T--) {
		int a, b; cin >> a >> b;
		if (a == 0) puts("1");
		else 
		cout << 2 * b + a + 1 << endl;
	}
	return 0;
}

B - Vlad and Candies

题意

\(a[i]\) 个数字 \(i\) , 其中 \(1 \leq i \leq n\)。 是否存在一种取数的方案让相邻取的两个数都不相同。
数据范围:\(1 \leq n \leq 2\times 10^5\)

题解

可以考虑一组构造,依次选取\(1 - n\),这样的话最后会剩余两个数字。
即最大值和次大值对应的 \(i_{max}, i_{min}\),那么这个时候判定就是\(a[i_{max}] - a[i_{min}]\)\(1\) 比大小的问题了。

时间复杂度: \(O(n \log_2 n)\) or \(O(n)\)

C++ 代码示例

# include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N];
int main() {
	int T; cin >> T;
	while (T--) {
		int n; cin >> n;
		for (int i = 1; i <= n; i++) cin >> a[i];
		sort(a + 1, a + 1 + n);
		if (a[n] - a[n - 1] > 1) puts("NO"); else puts("YES");
	}
	return 0;
}

C - Get an Even String

题意

如果一个字符串长度为偶数,且对于奇数位置和右侧一个位置的字符都相同,则该字符串是优美的。
给出字符串 \(s\),问要让串是优美的最少删除几个字符。
数据范围: \(1 \leq |s| \leq 2 \times 10^ 5\)

题解

删除最少字符可以转化为保留最多字符。

考虑动态规划。

\(f[i][j][0/1]\) 表示前 \(i\) 个字符,最后一个选择的字符为 \(j\) ,当前最后选择的字符作为新产生字符串的奇数位 \(1\) 还是偶数位 \(0\)

转移:

  • \(f[i][j][0 / 1] = f[i - 1][j][0 / 1]\)
  • \(f[i][j][0] = f[i - 1][j][1] + 1\)
  • \(f[i][j][1] = \max\{f[i - 1][k][0] + 1\}\)

上述的 \(k\) 那一维可以独立处理。

时间复杂度 \(O(26n)\)

C++ 代码示例

# include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int inf = 1e9;
int f[N][27][2];
int main() {
	int T; cin >> T;
	while (T--) {
		string s; cin >> s; int n = s.length();
		for (int i = 0; i < 26; i++) f[0][i][0] = 0, f[0][i][1] = -inf;
		for (int i = 1; i <= n; i++) {
			int res = -inf;
			for (int j = 0; j < 26; j++)
				res = max(res, f[i - 1][j][0] + 1);
			for (int j = 0; j < 26; j++) {
				f[i][j][0] = f[i - 1][j][0];
				f[i][j][1] = f[i - 1][j][1];
				f[i][s[i - 1] - 'a'][0] = max(f[i][s[i - 1] - 'a'][0], f[i - 1][s[i - 1] - 'a'][1] + 1);
				f[i][s[i - 1] - 'a'][1] = max(f[i][s[i - 1] - 'a'][1], res);
			}	
		}
		int ans = 0;
		for (int i = 0; i < 26; i++)
			ans = max(ans, f[n][i][0]);
		cout << n - ans << endl;	
	}
	return 0;
}

D - Maximum Product Strikes Back

题意

长度为 \(n\) 的整数序列 \(a_i(|a_i|\leq 2)\), 求积最大的连续子序列。
数据范围:\(1 \leq n \leq 2\times 10^5\)

题解

考虑贪心。

将元素全部删光,生成空字符串,积为 \(1\),所以答案一定大于等于 \(1\)

如果选取的连续子序列中包含至少一个 \(0\),那么积为 \(0\) 必然不是最优。

因此考虑将原序列按照 \(0\) 的位置分成极大串。对于每一个串,依次处理。

  • 如果当前字串乘积为正数,那么不用删除数。
  • 如果当前串乘积为负数,那么需要删除一个负数。

显然删除在当前序列的最左边第一个负数,或者删除当前序列的最右边第一个负数,剩余的数乘积最大。

由于最大值一定是 \(2\) 的幂次,所以我们只需要关心 \(2\) 在序列中出现几次,就可以比较不同子序列的大小。

时间复杂度为 \(O(n)\)

C++ 代码示例

# include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int inf = 1e9;
int n, a[N];
int main() {
	int t; cin >> t;
	while (t--) {
		cin >> n;
		for (int i = 1; i <= n; i++) cin >> a[i];
		vector<pair<int, int>>v;
		for (int i = 1; i <= n; i++) if (a[i] != 0) {
			int j = i + 1; while (j <= n && a[j] != 0) j++; j--;
			v.push_back({i, j});
			i = j;
		}
		int ans = -inf, ansl = -inf, ansr = -inf;
		for (auto tmp : v) {
			int l = tmp.first, r = tmp.second;
			int cnt1 = 0, cnt2 = 0;
			for (int i = l; i <= r; i++) {
				if (a[i] < 0) cnt1++;
				if (abs(a[i]) == 2) cnt2++;	
			}
			if (cnt1 % 2 == 0) {
				if (cnt2 > ans) ansl = l, ansr = r, ans = cnt2;
				continue;
			}
			int tm = 0;
			for (int i = l; i <= r; i++) {
				if (abs(a[i]) == 2) tm++;
				if (a[i] < 0) {
					if (cnt2 - tm > ans) ansl = i + 1, ansr = r, ans = cnt2 - tm;
					break;
				}
			}
			tm = 0;
			for (int i = r; i >= l; i--) {
				if (abs(a[i]) == 2) tm++;
				if (a[i] < 0) {
					if (cnt2 - tm > ans) ansl = l, ansr = i - 1, ans = cnt2 - tm;
					break;
				}
			}
		}
		if (ans < 0) cout << "0 " << n << endl;
		else cout << ansl - 1 << " " << n - ansr << endl;
	}
	return 0;
}

E - Matrix and Shifts

题意

一个 \(n \times n\)\(01\)矩阵,可以做如下 \(4\) 种免费的操作:
a. 将行循环上移,b. 将行循环下移,c. 将列循环左移,d. 将列循环右移
可以做如下代价为 \(1\) 的操作:将\(a[x][y]\) 异或 \(1\)
问将矩阵变成单位对角矩阵的最小代价。
数据范围:\(1 \leq n \leq 2000\)

题解

如果我们把行扩展两倍,列扩展两倍,那么免费变化的矩形,是\(2n \times 2n\)矩形中一个\(n\times n\)的子矩形。

为了计算通过免费操作,最多能把多少个 \(1\) 放在对角线上,我们可以处理这个\(2n \times 2n\)矩形的对角线。

求出每条对角线上连续长度为 \(n\)\(1\) 的个数的最大值。

然后可以计算出最小代价。

时间复杂度为 \(O(n^2)\)

C++ 代码示例

# include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int a[N << 1][N << 1];
vector<int>v[N * N];
int main() {
	int t; cin >> t;
	while (t--) {
		int n; cin >> n;
		for (int i = 1; i <= n; i++) {
			string s; cin >> s;
			for (int j = 0 ; j < n; j++)
				a[i][j + 1] = s[j] - '0';
		}
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++) {
				a[i + n][j] = a[i][j + n] = a[i + n][j + n] = a[i][j];
			}
		int tot = 0;
		for (int i = 1; i <= 2 * n; i++) {
			int x = 1, y = i;
			tot++; v[tot].clear();
			while (x <= 2 * n && y <= 2 * n) {
				v[tot].push_back(a[x++][y++]);
			}
		}
		for (int i = 1; i <= 2 * n; i++) {
			int x = i, y = 1;
			tot++; v[tot].clear();
			while (x <= 2 * n && y <= 2 * n) {
				v[tot].push_back(a[x++][y++]);
			}
		}
		int ans = 0;
		for (int i = 1; i <= tot; i++) if (v[i].size() >= n) {
			int cnt = 0;
			for (int j = 0; j < n; j++) cnt += v[i][j];
			ans = max(ans, cnt);
			for (int j = n; j < v[i].size(); j++) {
				if (v[i][j - n] == 1) cnt--;
				if (v[i][j] == 1) cnt++;
				ans = max(ans, cnt);
			}
		}
		int cnt = 0;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				if (a[i][j] == 1) cnt++;
		cout << n - ans + cnt - ans << endl;		
	}
	return 0;
}

F - Promising String

题意

一个字符串被称为好的,当且仅当经过若干次下列操作使得其中\(+,-\)字符个数相等。
操作:将两个连续的 \(-\) 变成 \(+\)
求出长度为 \(n\) 的字符串 \(s\) 有多少连续字串是好的。
easy version : \(1 \leq n \leq 3000\)
hasrd version : \(1 \leq n \leq 2\times 10^5\)

题解

观察到下列性质:

一个字符串是好的,当且仅当 \(+\) 的个数比 \(-\) 个数少,且差值为 \(3\) 的倍数。

  • easy version 暴力 \(O(n^2)\)

  • hard version 树状数组 \(O(n \log_2 n)\)

预处理前缀 \(+\) 的个数减去 \(-\) 的差值 的前缀和 \(s[i]\)

那么一个连续子序列可以表示为 \(s[j] - s[i]\)

需要满足 \(s[i] \leq s[j]\) 并且 \(s[i] \ \text{mod} \ 3 = s[j] \ \text{mod}\ 3\)

可以建 \(3\) 个树状数组,维护模 \(3\) 余数的 \(s[j]\) 值域信息即可。

C++ 代码示例

F1.cpp

# include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 10;
int a[N];
int main() {
	int t; cin >> t;
	while (t--) {
		int n; cin >> n;
		string s; cin >> s;
		for (int i = 1; i <= n; i++) a[i] = ((s[i - 1] == '+') ? -1 : 1);
		int ans = 0;
		for (int i = 1; i <= n; i++) {
			int cnt = 0;
			for (int j = i; j <= n; j++) {
				cnt += a[j];
				if (cnt >= 0 && cnt % 3 == 0) ans++;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

F2.cpp

# include <bits/stdc++.h>
# define int long long
# define lowbit(x) (x&(-x))
using namespace std;
const int N = 2e5 + 10;
int a[N], s[N], n;
struct BST {
	int c[N << 1];
	void update(int x, int d) {
		x += n + 1;
		for (;x <= 2 * n; x += lowbit(x)) c[x] += d;
	}
	int query(int x) {
		x += n + 1;
		int res = 0;
		for (; x ; x -= lowbit(x)) res += c[x];
		return res;
	}
}tr[3];
signed main() {
	int t; cin >> t;
	while (t--) {
		cin >> n;
		for (int i = 0; i < 3; i++)
			for (int j = -n; j <= n; j++)
				tr[i].c[j + n + 1] = 0;
		string th; cin >> th;
		for (int i = 1; i <= n; i++) {
			a[i] = ((th[i - 1] == '+') ? -1 : 1);
			s[i] = s[i - 1] + a[i];
		}
		int ans = 0;
		tr[0].update(0, 1); 
		for (int i = 1; i <= n; i++) {
			ans += tr[(s[i] % 3 + 3) % 3].query(s[i]);
			tr[(s[i] % 3 + 3) % 3].update(s[i], 1);
		}
		cout << ans << endl;
	}
	return 0;
}
posted @ 2022-04-01 10:07  Maystern  阅读(298)  评论(2编辑  收藏  举报