比赛链接:

https://codeforces.com/contest/1660

D. Maximum Product Strikes Back

题目大意:

一个整数序列,元素范围从 -2 到 2,可以删除它头部的一些元素或者尾部的一些元素,判断怎么删除才能使剩下的元素的最大。输出删除多少个头部的元素以及多少个尾部的元素。(整个序列都删除之后积为 1)

思路:

首先按照 0 的位置将整个序列分成几段,因为乘上 0 之后答案为 0,还不如整个序列都删掉。
接着每一段求一个和,若为正数,则直接和之前计算的最大值比较一下,若为负数,则要删除这一段中一个负数,根据贪心的策略,肯定删除该段左往右第一个负数及它左边的所有数,或者右往左第一个负数及它右边的所有数,选择一个最大的。
比较大小的时候只需要考虑 2 的数量即可,直接乘的话最大值为 \(2^200000\),会炸掉。

代码:

#include <bits/stdc++.h>
using namespace std;
int T = 1, n;
void solve(){
	cin >> n;
	vector <int> a(n + 1);
	for (int i = 1; i <= n; i ++ ) cin >> a[i];
	int p = 1, l = n, r = 0, ans = 0;
	for (int i = 1; i <= n; i ++ ){
		if (i == n || a[i] == 0){
			int cnt = 0, s = 1, t = i - (a[i] == 0);
			for (int j = p; j <= t; j ++ ){
				if (a[j] < 0) s *= -1;
				if (abs(a[j]) == 2) cnt++;
			}
			if (s > 0){
				if (cnt > ans){
					ans = cnt;
					l = p - 1;
					r = n - t;
				}
			}
			else {
				int dl = cnt, dr = cnt, ll = p, rr = t;
				for (; a[ll] > 0; ll ++ )
					if (abs(a[ll]) == 2)
						dl--;
				if (abs(a[ll]) == 2) dl--;
				ll++;
				for (; a[rr] > 0; rr -- )
					if (abs(a[rr]) == 2)
						dr--;
				if (abs(a[rr]) == 2) dr--;
				rr--;
				if (dl > dr){
					if (dl > ans){
						ans = dl;
						l = ll - 1;
						r = n - t;
					}
				}
				else{
					if (dr > ans){
						ans = dr;
						l = p - 1;
						r = n - rr;
					}
				}
			}
			p = i + 1;
		}
	}
	cout << l << " " << r << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> T;
	while (T--)
		solve();
	return 0;
}

E. Matrix and Shifts

题目大意:

一个 \(n * n\) 的每个元素为 0 或 1 的矩阵。
有两种操作:
一、所有行或者列周期移动,即所有行往下移动,第 \(n\) 行移动到第 1 行,或者所有行上移,第 1 行移动到第 \(n\) 行,列的移动也相同,这步操作免费。
二、选择一个格子,让它的值和 1 取异或,花费 1。
问最少花费多少使得矩阵变成单位矩阵。

思路:

第二步操作就是让 0 和 1 互相转化。为了变成单位矩阵,所有正对角线上的数要变成 1,其它值变为 0。
可以发现第一步操作不改变斜线上的 1 的数量。为了是第二步的花费最小,应该让 1 最多的那条斜线移动到正对角线。
所以直接计算每一条斜线上 1 的数量,让最大的那个作为正对角线,然后将斜线上的 0 变成 1,其它地方的 1 变成 0。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int T = 1, a[N][N], n;
int cal(int x, int y){
	int cnt = 0;
	while (y <= n){
		cnt += a[x][y];
		x = x % n + 1;
		y++;
	}
	return cnt;
}
void solve(){
	cin >> n;
	int s = 0, mx = 0;
	for (int i = 1; i <= n; i ++ )
		for (int j = 1; j <= n; j ++ ){
			char c;
			cin >> c;
			if (c == '1'){
				a[i][j] = 1;
				s++;
			}
			else a[i][j] = 0;
		}
	for (int i = 1; i <= n; i ++ )
		mx = max(mx, cal(i, 1));
	cout << n - mx + s - mx << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> T;
	while (T--)
		solve();
	return 0;
}

F1. Promising String (easy version)

题目大意:

一个只包含 '+' 和 '-' 的字符串,当一个子串中的 '+' 和 '-' 数量相等的话,它被称为平衡子串。若让子串中连续的两个 '-' 变成 '+' 后,子串平衡,这个子串也是平衡子串。计算该字符串中平衡子串的数量。

思路:

easy version,数据量小,先预处理一下 '+' 和 '-' 的数量的前缀和,然后枚举每一个区间就可以了。
当 '+' 和 '-' 的数量相等或者 '-' 的数量比 '+' 的数量多 3 的倍数个,都是平衡的,因为可以改变两个连续 '-' 变为 '+'。

代码:

#include <bits/stdc++.h>
using namespace std;
int T = 1, n;
string s;
void solve(){
	cin >> n >> s;
	vector <int> a(n + 1);
	for (int i = 0; i < n; i ++ )
		if (s[i] == '+') a[i + 1] = a[i] + 1;
		else a[i + 1] = a[i] - 1;
	int ans = 0;
	for (int i = 1; i <= n; i ++ )
		for (int j = i + 1; j <= n; j ++ )
			if (a[j] == a[i - 1] || ( (a[j] - a[i - 1]) % 3 == 0 && a[j] <= a[i - 1]) )
				ans++;
	cout << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> T;
	while (T--)
		solve();
	return 0;
}

F2 - Promising String (hard version)

思路:

暴力的基础上进行优化,若某段区间的 '-' 的数量减去 '+' 的数量的结果是 3 的倍数,那么这一段区间一定是可以的。
计算出 '-' 的数量的前缀和,那么要求的就是在 \(i\) 之前的所有前缀和小于它且差值为 3 的倍数的数,即找到 \(j\),满足 \(a[j] <= a[i]\)\(j < i\)\((a[j] - a[i])\) % 3 == 0 。
树状数组来维护这个值的计算。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
#define LL long long
string s;
LL T = 1, n, tree[3][2 * N + 1], a[N];
LL lowbit(LL k){
	return k & -k;
}
void update(LL x, LL k, LL p){
	while (x <= 2 * n + 1){
		tree[p][x] += k;
		x += lowbit(x);
	}
}
LL query(LL x, LL p){
	LL t = 0;
	while( x > 0 ){
		t += tree[p][x];
		x -= lowbit(x);
	}
	return t;
}
void solve(){
	cin >> n >> s;
	for (int i = 0; i <= 2 * n + 1; i ++ )
		tree[0][i] = tree[1][i] = tree[2][i] = 0;
	for (int i = 0; i < n; i ++ )
		a[i + 1] = a[i] + (s[i] == '-' ? 1 : -1);
	LL ans = 0;
	update(n + 1, 1, 0);
	for (int i = 1; i <= n; i ++ ){
		LL k = (a[i] % 3 + 3) % 3;
		ans += query(n + a[i] + 1, k);
		update(n + a[i] + 1, 1, k);
	}
	cout << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> T;
	while (T--)
		solve();
	return 0;
}
posted on 2022-04-10 21:59  Hamine  阅读(43)  评论(0编辑  收藏  举报