Codeforces Round #790 (Div. 4) 題解
A. Lucky?
沒什麼好說的, 直接模擬即可.
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0; char c = getchar(); bool f = 1; while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); } while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return (f ? x : -x); } const int N = 1e5 + 10; int n, a[N]; string s; int main() { n = read(); while(n--) { cin >> s; if(s[0] + s[1] + s[2] == s[5] + s[4] + s[3]) puts("YES"); else puts("NO"); } return 0; }
複雜度: \(O(n)\)
B. Equal Candies
由於每一盒的糖果只能增不能減, 並且要讓每一盒糖果都一樣, 顯然直接讓每一盒都等於 最小的糖果數 , 於是答案就等於 \(\sum _{i = 1} ^n a_i - n * \mathop{min\{a_i\}}\limits_{1 \leq i \leq n}\)
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0; char c = getchar(); bool f = 1; while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); } while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return (f ? x : -x); } const int N = 1e5 + 10; int t, n, now, m, sum; int main() { t = read(); while(t--) { n = read(); sum = 0; m = 0x3f3f3f3f; for(int i = 1; i <= n; ++i) { now = read(); m = min(m, now); sum += now; } printf("%d\n", sum - n * m); } return 0; }
複雜度: \(O(n)\)
C. Most Similar Words
先看資料範圍: \(1 \leq t \leq 100, 2 \leq n \leq 50, 1 \leq m \leq 8\) .
這個範圍非常小, 於是考慮暴力.首先 \(O(n^2)\) 遍歷任意兩個字串, 再 \(O(m)\) 計算出距離,取最小值即可.
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0; char c = getchar(); bool f = 1; while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); } while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return (f ? x : -x); } const int N = 1e5 + 10; int t, n, m, res; string s[N]; int main() { t = read(); while(t--) { res = 0x3f3f3f3f; n = read(); m = read(); for(int i = 1; i <= n; ++i) cin >> s[i]; for(int i = 1; i <= n ;++i) { for(int j = i + 1; j <= n; ++j) { int tot = 0; for(int p = 0; p < m; ++p) tot += abs(s[i][p] - s[j][p]); res = min(res, tot); } } printf("%d\n", res); } return 0; }
複雜度: \(O(n^2m)\)
D. X-Sum
還是先看資料範圍: \(1 \leq t \leq 1000, 1 \leq n \leq 200, 1 \leq m \leq 200, 1 \leq \sum nm \leq 4 \times 10^4\).
注意最後一條 \(nm\) 的限制, 明顯提醒我們複雜度裡有 \(nm\).
於是先遍歷每一個格子就可以把 \(nm\) 弄出來, 再計算所有能攻擊到的格子總和 \(O(n + m)\)
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e5 + 10;
int t, n, m, k, res, cnt, mp[205][205];
inline bool check(int x, int y) { return x >= 1 && x <= n && y >= 1 && y <= m; }//防止走出棋盤
int main() {
t = read();
while(t--) {
n = read(); m = read(); k = max(n, m);
res = 0;
for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) mp[i][j] = read();
for(int i = 1; i <= n ;++i) {
for(int j = 1; j <= m; ++j) {
cnt = 0;
for(int p = 1; p <= k; ++p) {
if(check(i + p, j + p)) cnt += mp[i + p][j + p];
if(check(i - p, j - p)) cnt += mp[i - p][j - p];
if(check(i + p, j - p)) cnt += mp[i + p][j - p];
if(check(i - p, j + p)) cnt += mp[i - p][j + p];
}
res = max(res, cnt + mp[i][j]);
}
}
printf("%d\n", res);
}
return 0;
}
複雜度: \(O(nm(n+m))\)
E. Eating Queries
顯然, 每次查詢的時候, 他都會想吃掉 含糖量最高 的那一個, 所以我們需要從大到小排序.
又因為有多次查詢, 所以我們不必每次都重新算一遍, 於是 \(O(n)\) 維護一個數組 \(d\) 使得 \(\forall 1 \leq i \leq n, d_i = d_{i - 1} + a_i(a\ is\ sorted)\)
那麼每次查詢的時候只需要找到最小的 \(d_i\) 滿足 \(d_i \geq q(q\ is\ asking)\) , 看起來是不是很熟悉? 沒錯, 可以二分搜尋, 所以對於每次詢問只需要 \(log(n)\) 的時間了!
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;
int t, n, m, q, res, a[N], d[N];
inline bool check(int x, int y) { return x >= 1 && x <= n && y >= 1 && y <= m; }
int main() {
t = read();
while(t--) {
n = read(); m = read();
for(int i = 1; i <= n; ++i) a[i] = read();
sort(a + 1, a + 1 + n, greater<int>());
for(int i = 1; i <= n; ++i) d[i] = d[i - 1] + a[i];
while(m--) {
res = lower_bound(d + 1, d + 1 + n, read()) - d;
printf("%d\n", res == n + 1 ? -1 : res);
}
}
return 0;
}
複雜度: \(O(nqlogn)\)
F. Longest Strike
首先顯然維護每個數出現的次數, 由於 \(1 \leq a_i \leq 10^9\) 所以不能使用桶, 於是使用 map \(n logn\) 統計(還有一個好處就是自動從小到大排序, 等下會用到).
然後再從小到大遍歷 map , 由於 map 從小到大, 正好滿足我們的需求, 可以順理成章地查詢一段最長的連續的區間, 然後更新.
思路很簡單, 程式碼很複雜. /wn
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;
int t, n, m, l, r, maxl, minv, maxv, s, last, a[N];
//這裡使用m來代表題目中的k, maxl代表當前最長的區間長度, minv最小的ai, maxv最大的ai, s當前區間的長度, last上一個元素
map<int, int> mp;
//key元素, value出現次數
int main() {
t = read();
while(t--) {
n = read(); m = read();
mp.clear(); maxl = 0; minv = 0x3f3f3f3f; maxv = 0; s = 0;
for(int i = 1; i <= n; ++i) a[i] = read(), ++mp[a[i]], minv = min(minv, a[i]), maxv = max(maxv, a[i]);
last = minv - 1;//第一次認為是連續的
for(auto v : mp) {
int i = v.first;
if(i == maxv && v.second >= m && maxl < s + 1 && i == last + 1) maxl = s + 1, l = i - s, r = i;//最後一個不一定更新的到, 需要特判, 但如果前面不連續, 這裡也判不掉, 在輸出的時候能解決
if(v.second >= m && i == last + 1) ++s;//出現次數合格, 並且滿足連續
else {
if(maxl < s) maxl = s, l = last + 1 - s, r = last;//更新答案
if(v.second >= m) s = 1;//如果次數合格但只是不連續, 則可以作為新一個區間的開頭
else s = 0;//否則啥也不是, 從零開始
}
last = i;//記錄上一個元素
}
if(maxl) printf("%d %d\n", l, r);
else if(mp[maxv] >= m) printf("%d %d\n", maxv, maxv);//最後不連續, 但是次數合格, 並且前面沒有任何一個次數合格的話, 輸出最後一個
else puts("-1");//否則是真的無解
}
return 0;
}
複雜度: \(O(nlogn)\)
G. White-Black Balanced Subtrees
我們規定 W 代表 \(1\) , B 代表 \(0\) , \(a_u\) 表示 \(u\) 這個節點的狀態(是 \(0\) 還是 \(1\)) , \(dp1_u\) 表示以 \(u\) 為根的子樹含有 \(1\) 的個數, \(dp0_u\) 表示以 \(u\) 為根的子樹含有 \(0\) 的個數, 不用腦子都能想出怎麼轉移:
\(dp1_u = \mathop{\sum dp_v} \limits_{v\ is\ u's\ son} + a_u\)
\(dp0_u = \mathop{\sum dp_v} \limits_{v\ is\ u's\ son} +\ !a_u\)
這裡的 \(!\) 表示程式裡的取反(原諒我不知道怎麼表示)
最後如果 \(u\) 滿足 \(dp1_u = dp0_u\) 時, 結果需要加 \(1\).
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;
int t, n, res, dp1[N], dp0[N];
bool f[N];
string s;
int cnt = -1, head[N];
struct edge {
int to, nxt;
} e[N << 1];
inline void add(int u, int v) {
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
inline void adde(int u, int v) { add(u, v); add(v, u); }
inline void dfs(int u, int fa) {
if(f[u]) dp1[u] = 1;
else dp0[u] = 1;
for(int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].to;
if(v == fa) continue;
dfs(v, u);
dp1[u] += dp1[v];
dp0[u] += dp0[v];
}
if(dp1[u] == dp0[u]) ++res;
}
int main() {
t = read();
while(t--) {
n = read(); res = 0;
for(int i = 0; i <= n; ++i) head[i] = -1;
for(int i = 0; i <= n; ++i) dp1[i] = dp0[i] = 0;
for(int i = 2; i <= n; ++i) adde(i, read());
cin >> s;
for(int i = 0; i < n; ++i)
f[i + 1] = s[i] == 'W';
dfs(1, 0);
printf("%d\n", res);
}
return 0;
}
複雜度: \(O(n)\)
H1. Maximum Crossings (Easy Version)
這個題把線段換成點想必大家都會做, 那麼怎麼變換一下呢?
首先顯然如果 \(i < j\) 並且 \(a_i > a_j\) 必定相交(這個也就是逆序對)
那有的人說了: 給的樣例中不也有 \(a_i = a_j\) 相交的嗎?
當然, 如果 \(i < j\) 並且 \(a_i = a_j\) 可能相交.
考慮如下情況(只考慮1和3兩條線段, 2是為了美觀而存在的):
可以看到, 它們並沒有相交.
但我們改變一下相對位置, 就會驚奇地發現, 他們居然能相交!
又因為題目要求最大相交, 所以答案等於滿足 \(i < j, a_i \geq a_j\) 的個數.
這題 \(1 \leq n \leq 1000\) , 範圍較小, 所以可以用 \(O(n^2)\) 的雙指標來做.
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;
int t, n, res, a[N];
int main() {
t = read();
while(t--) {
n = read(); res = 0;
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j)
if(a[i] >= a[j]) ++res;
printf("%d\n", res);
}
return 0;
}
複雜度: \(O(n^2)\)
H2. Maximum Crossings (Hard Version)
這題與 H1 只差了一個\(1 \leq n \leq 2 \times 10^5\) .於是考慮優化.
這裡我用的樹狀陣列求的逆序對, 歸併排序也可以做.
注意要開 long long
.
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x = 0; char c = getchar(); bool f = 1;
while(c < '0' || c > '9') { if(c == '-') f = 0; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return (f ? x : -x);
}
const int N = 1e6 + 10;
int t, n, res, len, a[N], tree[N];
inline int lowbit(int &x) { return x & -x; }
inline void updata(int x) { while(x <= n) { ++tree[x]; x += lowbit(x); } }
inline int pre(int x) { int res = 0; while(x) { res += tree[x]; x -= lowbit(x); } return res; }
inline int ask(int l, int r) { return pre(r) - pre(l - 1); }
signed main() {
t = read();
while(t--) {
n = read(); res = 0;
for(int i = 1; i <= n; ++i) a[i] = read(), tree[i] = 0;
for(int i = n; i >= 1; --i) {
res += ask(1, a[i]);
updata(a[i]);
}
printf("%lld\n", res);
}
return 0;
}
複雜度: \(O(nlogn)\)