1. 程式人生 > 其它 >Codeforces Round #780 (Div. 3)(down)

Codeforces Round #780 (Div. 3)(down)

比賽連結:

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;
}