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