Codeforces 938 解题 Div 3
太久没做题了, 先拿个Div 3开刷。
Codeforces Round 938 Div 3:
A:
首先我们可以发现到若2 * a <= b, 那么我们n >= 2 的时候,拿b是最优的。
若我们现在拿了许多的2以后, 我们还有余数 = 1, 我们就用a补上。

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 void solve() { 6 int n, a, b; cin >> n >> a >> b; 7 if (2 * a <= b) cout << n * a << '\n'; 8 else cout << (n / 2) * b + (n & 1) * a << '\n'; 9 } 10 int main() { 11 ios::sync_with_stdio(false); 12 cin.tie(nullptr); cout.tie(nullptr); 13 14 int t; cin >> t; 15 while (t--) { 16 solve(); 17 } 18 }
B:
B 非常简单就是求你的数和他的数是不是完全重合的。
我当时是直接用multiset来搞,没找到就是不一样,找到的话就移除掉一个数。
其实也可以直接推进一个vector 然后sort再一个一个对比。

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 void solve() { 6 multiset<int> S; 7 int n, c, d; cin >> n >> c >> d; 8 int mn = 1E9 + 9; 9 for (int i = 1; i <= n * n; i++) { 10 int x; cin >> x; 11 S.insert(x); 12 mn = min(mn, x); 13 } 14 bool ok = true; 15 for (int i = 1; i <= n; i++) { 16 int o = mn + (i - 1) * d; 17 for (int j = 1; j <= n; j++) { 18 int p = o + (j - 1) * c; 19 if (S.find(p) == S.end()) ok = false; 20 else S.erase(S.find(p)); 21 } 22 } 23 if (ok) cout << "YES\n"; 24 else cout << "NO\n"; 25 } 26 int main() { 27 ios::sync_with_stdio(false); 28 cin.tie(nullptr); cout.tie(nullptr); 29 30 int t; cin >> t; 31 while (t--) { 32 solve(); 33 } 34 }
C:
C也是一眼的题。我们首先可以发现到前面的数承受了ceil(K / 2) (向上取整), 后面的数则是承受了 K / 2。
那么我们可以直接暴力这个承受伤害的过程。
我为了写代码方便我特判了一下当 K >= sum(A) 的情况。

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 void solve() { 6 int n; i64 k; cin >> n >> k; 7 vector<i64> a(n); 8 for (int i = 0; i < n; i++) cin >> a[i]; 9 if (k >= accumulate(a.begin(), a.end(), 0LL)) { 10 cout << n << '\n'; 11 return; 12 } 13 int p = 0; i64 rem = (k + 1) / 2; 14 while (rem) { 15 i64 use = min(rem, a[p]); 16 a[p] -= use; 17 rem -= use; 18 if (a[p] == 0) p++; 19 } 20 p = n - 1; rem = k / 2; 21 while (rem) { 22 i64 use = min(rem, a[p]); 23 a[p] -= use; 24 rem -= use; 25 if (a[p] == 0) p--; 26 } 27 int ans = 0; 28 for (int i = 0; i < n; i++) if (a[i] != 0) ans++; 29 cout << n - ans << '\n'; 30 } 31 int main() { 32 ios::sync_with_stdio(false); 33 cin.tie(nullptr); cout.tie(nullptr); 34 35 int t; cin >> t; 36 while (t--) { 37 solve(); 38 } 39 }
D:
啊这题一看就感觉会了。
首先我们数字i的贡献其实就是min(ocA[i], ocB[i])。
ocA[i]表示i出现在A的次数, B相同。
那我们想要移除掉就减上以前的贡献,做出我们想要的改变, 再加上新的贡献。
那我们就可以算多少个i满足条件。

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 void solve() { 6 int n, m, k; cin >> n >> m >> k; 7 vector<int> a(n + 1), b(m + 1); 8 for (int i = 1; i <= n; i++) cin >> a[i]; 9 for (int i = 1; i <= m; i++) cin >> b[i]; 10 map<int,int> ocA, ocB; 11 for (int i = 1; i <= m; i++) ocB[b[i]]++; 12 int cur = 0; 13 function<void(int,int)> work = [&] (int x, int op) { 14 cur -= min(ocA[x], ocB[x]); 15 ocA[x] += op; 16 cur += min(ocA[x], ocB[x]); 17 }; 18 int ans = 0; 19 for (int i = n; i >= 1; i--) { 20 if (i + m <= n) work(a[i + m], -1); 21 work(a[i], +1); 22 if (i <= n - m + 1 && cur >= k) ans++; 23 } 24 cout << ans << '\n'; 25 } 26 int main() { 27 ios::sync_with_stdio(false); 28 cin.tie(nullptr); cout.tie(nullptr); 29 30 int t; cin >> t; 31 while (t--) { 32 solve(); 33 } 34 }
E:
首先看到我们可以做到O(N ^ 2)。
那么我们枚举从大到小,若当前的数满足条件,我们这不需要再继续往下搜了。
那么我们考虑怎么判断一个数行不行。让当前的x表示枚举的数。
我们从后面开始判断。若s[i]当前 = 0, 我们则需要反转他 (和前面的)。那么我们其实需要维护一个suffix sum来判断当前是否做了反转。
若我们的s[i] = 0, 且i < x, 那我们就可以说这一个数x不满足条件了。

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 void solve() { 6 int n; cin >> n; 7 string s; cin >> s; 8 s = ' ' + s; 9 function<bool(int)> work = [&] (int x) { 10 vector<int> suf(n + 1); 11 for (int i = n; i >= 1; i--) { 12 int o = s[i] - '0'; 13 if (i + 1 <= n) suf[i] += suf[i + 1]; 14 o += suf[i]; 15 o %= 2; 16 if (o == 0) { 17 suf[i]++; 18 if (i - x >= 0) suf[i - x]--; 19 else return false; 20 } 21 } 22 return true; 23 }; 24 if (count(s.begin(), s.end(), '1') == n) { 25 cout << n << '\n'; 26 return; 27 } 28 for (int i = n; i >= 1; i--) { 29 if (work(i)) { 30 cout << i << '\n'; 31 return; 32 } 33 } 34 cout << 0 << '\n'; 35 } 36 int main() { 37 ios::sync_with_stdio(false); 38 cin.tie(nullptr); cout.tie(nullptr); 39 40 int t; cin >> t; 41 while (t--) { 42 solve(); 43 } 44 }
F:
这题不错虽然感觉没那么难。
首先我们可以看到当一个数的数量是偶数,他是没有贡献的。
那么我们可以打一个表来看什么时候我们xor = 0。
我们发现到我们要么是1, 2, 3, 4: 偶,偶,偶,偶, 要么是1, 2, 3, 4:奇,奇,奇,偶。
发现到其实若我们想要花费更加多的数来满足奇,奇,奇,偶的条件。
所以我们贪心取偶,偶,偶,偶。那么贡献就是 a/2 + b/2 + c/2 + d/2这样。
但是我们有可能最后有1, 1, 1, 0或者1, 1, 1, 1的数。那么我们在这个情况下取奇,奇,奇,偶,答案+1

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 void solve() { 6 int a, b, c, d; 7 cin >> a >> b >> c >> d; 8 int ans = (a / 2) + (b / 2) + (c / 2) + (d / 2); 9 a %= 2; b %= 2; c %= 2; d %= 2; 10 if (a == 1 && b == 1 && c == 1) ans++; 11 cout << ans << '\n'; 12 // all even or 1, 2, 3 is odd 13 } 14 int main() { 15 ios::sync_with_stdio(false); 16 cin.tie(nullptr); cout.tie(nullptr); 17 18 int t; cin >> t; 19 while (t--) { 20 solve(); 21 } 22 }
G:
G有点傻了,不知道为什么vector开在function里面会爆。渍渍渍。
考虑枚举每一个数x是否可以到达(n, m)。首先我们让p[i][j] = a[i][j] % x 是不是 0。
然后呢,我们就可以非常简单的判断(i, j)是否可以被到达。这个应该不难。
现在发现到我们其实不需要枚举那么多数,我们枚举每个a[1][1]的因子就行。
我们直接sieve 然后暴力就可以。技术含量不高。

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 int gcd(int x, int y) { 6 if (y == 0) return x; 7 return gcd(y, x % y); 8 } 9 vector<int> _div[1000003]; 10 void solve() { 11 int n, m; cin >> n >> m; 12 vector<vector<int>> a(n + 1, vector<int>(m + 1)); 13 auto ok = a; 14 for (int i = 1; i <= n; i++) { 15 for (int j = 1; j <= m; j++) { 16 cin >> a[i][j]; 17 } 18 } 19 auto work = [&](int x) { 20 for (int i = 1; i <= n; i++) { 21 for (int j = 1; j <= m; j++) { 22 ok[i][j] = 0; 23 } 24 } 25 ok[1][1] = 1; 26 for (int i = 1; i <= n; i++) { 27 for (int j = 1; j <= m; j++) { 28 if (a[i][j] % x) { 29 ok[i][j] = 0; 30 continue; 31 } 32 if (i - 1 >= 1) ok[i][j] |= ok[i - 1][j]; 33 if (j - 1 >= 1) ok[i][j] |= ok[i][j - 1]; 34 } 35 } 36 return ok[n][m]; 37 }; 38 int ans = 1; 39 for (auto v : _div[a[1][1]]) if (work(v)) ans = max(ans, v); 40 cout << ans << '\n'; 41 } 42 int main() { 43 ios::sync_with_stdio(false); 44 cin.tie(nullptr); cout.tie(nullptr); 45 46 for (int i = 1; i <= 1E6; i++) { 47 for (int j = i; j <= 1E6; j += i) { 48 _div[j].push_back(i); 49 } 50 } 51 52 int t; cin >> t; 53 while (t--) { 54 solve(); 55 } 56 }
H:
一眼就知道R不能取值太高,直接暴力算R的上限 = 12。
那么我们就可以快乐的暴力算差值了。
我们枚举每个有塔的地方,让use[r] = 多了多少伤害 - 3 ^ r。
那么根据题目我们不能够拿同一个R,我们就可以维持一个dp[S] = 最高差值和,S在2进制表示第i个r有没有被使用了。
这就是什么bit DP。
答案就是dp里的最大值。

1 #include<bits/stdc++.h> 2 using namespace std; 3 using i64 = long long; 4 5 void solve() { 6 int n, m, k; cin >> n >> m >> k; 7 // max R = 12 8 vector<vector<char>> grid(n + 1, vector<char>(m + 1)); 9 vector<vector<int>> P(n + 1, vector<int>(m + 1)); 10 for (int i = 1; i <= n; i++) { 11 for (int j = 1; j <= m; j++) { 12 cin >> grid[i][j]; 13 } 14 } 15 for (int i = 1; i <= k; i++) { 16 int x, y, p; 17 cin >> x >> y >> p; 18 P[x][y] += p; 19 } 20 vector<i64> dp((1 << 13) + 50, -1E18); 21 dp[0] = 0; 22 for (int i = 1; i <= n; i++) { 23 for (int j = 1; j <= m; j++) { 24 if (P[i][j] == 0) continue; 25 vector<i64> dif(13); 26 i64 pw = 1; 27 for (int r = 1; r <= 12; r++) { 28 pw *= 3; 29 i64 dmg = 0; 30 for (int x = 1; x <= n; x++) { 31 for (int y = 1; y <= m; y++) { 32 if (grid[x][y] == '#' && (i - x) * (i - x) + (j - y) * (j - y) <= r * r) { 33 dmg += P[i][j]; 34 } 35 } 36 } 37 dif[r] = dmg - pw; 38 } 39 for (int bit = (1 << 13); bit >= 0; bit--) { 40 for (int x = 0; x < 13; x++) { 41 if (bit >> x & 1) { 42 dp[bit] = max(dp[bit], dp[bit ^ (1 << x)] + dif[x]); 43 } 44 } 45 } 46 } 47 } 48 // for (int i = 0; i < (1 << 13); i++) cout << dp[i] << ' '; 49 // cout << '\n'; 50 cout << *max_element(dp.begin(), dp.end()) << '\n'; 51 } 52 int main() { 53 ios::sync_with_stdio(false); 54 cin.tie(nullptr); cout.tie(nullptr); 55 56 int t; cin >> t; 57 while (t--) { 58 solve(); 59 } 60 }
嗯,不错的题目。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端