Educational Codeforces Round 28

1|0A. Curriculum Vitae


因为1之后不能出现0,所以求一下前缀0的个数和后缀1的个数,然后枚举第一个1的位置即可

#include <bits/stdc++.h> using namespace std; #define int long long int32_t main() { ios::sync_with_stdio(false) , cin.tie(nullptr) , cout.tie(nullptr); int n; cin >> n; vector<int> a(n) , b(n) , c(n); for( auto & i : a ) cin >> i; b[0] = ( a[0] == 0 ); for( int i = 1 ; i < n ; i ++ ) b[i] = b[i-1] + ( a[i] == 0 ); c[n-1] = a[n-1]; for( int i = n-2 ; i >= 0 ; i -- ) c[i] = c[i+1] + a[i]; int res = max( b.back() , c.front() ); for( int i = 1 ; i < n ; i ++ ) res = max( res , b[i-1] + c[i] ); cout << res; return 0; }

2|0B. Math Show


先枚举几组,剩下的贪心选就好了。

#include <bits/stdc++.h> using namespace std; #define int long long int32_t main(){ int n , m , k , res = 0 , cnt = 0; cin >> n >> m >> k; vector<int> a(m); for( auto & i : a ) cin >> i , cnt += i; sort( a.begin() , a.end() ); for( int i = 0 , ans , p ; i <= n && i * cnt <= k ; i ++ ){ ans = i * (m+1) , p = k - i * cnt; for( auto j : a){ int t = min( n - i , p / j ); ans += t , p -= t * j ; } res = max( res , ans ); } cout << res << "\n"; return 0; }

3|0C. Four Segments


把三个分割符记为x,y,zres=sum(0,x)sum(x,y)+sum(y,z)sum(z,n)

然后发现当y确定时,x,z的选择相互没有影响,所以可以O(N2)的计算对于每一个y最优的sum(0,x)sum(x,y)x出现在哪里,z同理,最后枚举一下选择哪个y即可

#include <bits/stdc++.h> using namespace std; #define int long long int32_t main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n; cin >> n; vector<int> v(n), pre(n); for (auto &i: v) cin >> i; pre[0] = v[0]; for (int i = 1; i < n; i++) pre[i] = pre[i - 1] + v[i]; auto f = [pre](int l, int r) { if (l == r) return 0ll; if (l == 0) return pre[r - 1]; return pre[r - 1] - pre[l - 1]; }; vector<int> ya(n + 1), yx(n + 1), yb(n + 1), yz(n + 1); for (int y = 0; y <= n; y++) { ya[y] = f(0, y), yx[y] = y; for (int x = y - 1, a; x >= 0; x--) { a = f(0, x) - f(x, y); if (a > ya[y]) ya[y] = a, yx[y] = x; } } for (int y = 0; y <= n; y++) { yb[y] = f(y, n), yz[y] = n; for (int z = n - 1, b; z >= y; z--) { b = f(y, z) - f(z, n); if (b > yb[y]) yb[y] = b, yz[y] = z; } } int rv = LLONG_MIN, rx, ry, rz; for (int y = 0, yv; y <= n; y++) { yv = ya[y] + yb[y]; if (yv > rv) rv = yv, rx = yx[y], ry = y, rz = yz[y]; } cout << rx << " " << ry << " " << rz << "\n"; return 0; }

4|0D. Monitor


其实就是一个矩形一开始是全部是正无穷,然后修改某些点的值,询问所有k×k的子矩形的最大值最小是多少。

首先可以滑动窗口的方式计算出每一行所有长度为k的子区间的最大值,把这些结果全部存到一个数组中,再用滑动窗口计算每一个k×k的子矩形的最大值,最后取最小值即可。

#include <bits/stdc++.h> using namespace std; #define int long long int32_t main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n, m, k, q; cin >> n >> m >> k >> q; vector<vector<int>> a(n + 1, vector<int>(m + 1, LLONG_MAX)); for (int x, y, z; q; q--) cin >> x >> y >> z, a[x][y] = z; vector<vector<int>> b(n + 1, vector<int>(m + 1)); for (int i = 1; i <= n; i++) { multiset<int> s; for (int j = 1; j < k; j++) s.insert(a[i][j]); for (int j = k; j <= m; j++) { s.insert(a[i][j]); b[i][j] = *s.rbegin(); s.erase(s.lower_bound(a[i][j - k + 1])); } } int res = LLONG_MAX; for (int j = k; j <= m; j++) { multiset<int> s; for (int i = 1; i < k; i++) s.insert(b[i][j]); for (int i = k; i <= n; i++) { s.insert(b[i][j]); res = min(res, *s.rbegin()); s.erase(s.lower_bound(b[i - k + 1][j])); } } if (res == LLONG_MAX) cout << "-1\n"; else cout << res << "\n"; return 0; }

5|0E. Chemistry in Berland


树形DP,f[i]表示节点i在满足子树内全部够用的情况下还余/欠多少,递归的计算出每个节点的子节点的f[i]多余的就转化给父节点,不够就从父节点转换,最后检查根节点是否够用即可。

要注意的是转换的过程中很容易爆long long,最大值应该是109×105,所以要用些特殊的方法来判断。

#include <bits/stdc++.h> using namespace std; #define int long long #define ll long long int n; vector<int> a, b, f; vector<vector<pair<int, int>>> e; constexpr int inf = 1e18; int read() { int x = 0, f = 1, ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') f = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } void dfs(int x) { f[x] = b[x] - a[x]; for (auto [y, v]: e[x]) { dfs(y); if (f[y] < 0) { if( inf / v < - f[y] ) f[x] = - inf; else f[x] += f[y] * v, f[y] = 0; if( f[x] < -inf ) f[x] = -inf; } else f[x] += f[y], f[y] = 0; } } int32_t main() { n = read(); a = vector<int>(n + 1), b = vector<int>(n + 1); f = vector<int>(n + 1); for (int i = 1; i <= n; i++) b[i] = read(); for (int i = 1; i <= n; i++) a[i] = read(); e = vector<vector<pair<int, int>>>(n + 1); for (int i = 2, fa, v; i <= n; i++) fa = read(), v = read(), e[fa].emplace_back(i, v); dfs(1); if (f[1] >= 0) printf("YES\n"); else printf("NO\n"); return 0; }

6|0F. Random Query


题目期望是假的,实际上我们只要计算所有的子区间中的数的种类数之和最后除以子区间数即可。

要计算区间内数的种类显然不可取,因为至少有O(N2)的复杂度。规定区间内每种数最后一次出现有效,这样的话我们来考虑每个数在哪些区间里面会产生贡献。我们计算出nxt[i]表示i下一次出现的位置,那么数字i向左一直到1都可以产生贡献,向右到nxt[i]1都可以产生贡献,所有i产生贡献的区间有i×(nex[i]i)

注意l>r也可以产生贡献,但是l=r只能产生一次贡献。

#include <bits/stdc++.h> using namespace std; #define int long long int read() { int x = 0, f = 1, ch = getchar(); while ((ch < '0' || ch > '9') && ch != '-') ch = getchar(); if (ch == '-') f = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x * f; } int32_t main() { int n = read() , res = 0; vector<int> a(n+1) , nxt( 1e6+1 , n+1 ); for( int i = 1 ; i <= n ; i ++ ) a[i] = read(); for( int i = n ; i >= 1 ; i -- ) res += i * (nxt[a[i]] - i) , nxt[a[i]] = i; res = res * 2 - n; printf("%.6lf\n" , (double) res / double(n*n) ); return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/17551154.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(24)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)
点击右上角即可分享
微信分享提示