Codeforces Round 966 (Div. 3)
Codeforces Round 966 (Div. 3)
A. Primary Task
思路
\(false\) 的情况:1、\(s.size()\le 2\) ;2、\(s\) 不以10
开头;3、\(s\) 2位以后得字符串转整型后小于 \(2\) 或者含有前导零。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { string s; cin >> s; if (s.size() <= 2) { cout << "NO\n"; return ; } cout << (s.substr(0, 2) == "10" && stoi(s.substr(2)) >= 2 && to_string(stoi(s.substr(2))) == s.substr(2) ? "YES" : "NO") << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
B. Seating in a Bus
思路
用 \(set\) 维护当前位是不是等于已插入数 最大+1
或者最小-1
。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; cin >> n; vector<int> a(n + 1); for (int i = 1; i <= n; i ++) { cin >> a[i]; } set<int> s; s.insert(a[1]); for (int i = 2; i <= n; i ++) { if (a[i] == *s.rbegin() + 1 || a[i] == *s.begin() - 1) { s.insert(a[i]); } else { cout << "NO\n"; return ; } } cout << "YES\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
C. Numeric String Template
思路
预处理数字串中相同数字的所有位置,然后对字符串也进行相应的处理,最后判断字符串的位置是否合法即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; cin >> n; map<int, int> mp; vector<int> a(n); vector ve(n, vector<int>()); for (int i = 0; i < n ; i ++) { cin >> a[i]; if (!mp.count(a[i])) { mp[a[i]] = i; ve[i].push_back(i); } else { ve[mp[a[i]]].push_back(i); } } int m; cin >> m; while (m--) { string s; cin >> s; if (s.size() != n) { cout << "NO\n"; continue; } map<int, int> mp1; vector Ve(n, vector<int>()); for (int i = 0; i < s.size(); i ++) { if (!mp1.count(s[i])) { mp1[s[i]] = i; Ve[i].push_back(i); } else { Ve[mp1[s[i]]].push_back(i); } } cout << (Ve == ve ? "YES\n" : "NO\n"); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
D. Right Left Wrong
思路
要尽可能地获取最高分,那么最左边的L
就要尽可能地去匹配最右边的R
,处理出两者的位置,然后用前缀和求和即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; cin >> n; vector<i64> a(n + 1), pre(n + 1); for (int i = 1; i <= n; i ++) { cin >> a[i]; pre[i] = pre[i - 1] + a[i]; } string s; cin >> s; s = " " + s; vector<int> l, r; for (int i = n; i >= 1; i --) { if (s[i] == 'R') { r.push_back(i); } } for (int i = 1; i <= n; i ++) { if (s[i] == 'L') { l.push_back(i); } } if (l.empty() || r.empty()) { cout << 0 << '\n'; return ; } i64 ans = 0; int idx = 0; for (auto i : l) { if (r[idx] < i || idx >= r.size()) break; ans += pre[r[idx]] - pre[i - 1]; idx ++; } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
E. Photoshoot for Gorillas
思路
注意到 $1\le n\cdot m\le 1e5 $,那么可以直接二维差分前缀和处理矩形的覆盖范围,然后将覆盖的值取出从大到小排序和 \(a_i\) 对应即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { i64 n, m, k, w; cin >> n >> m >> k >> w; vector<i64> a(w); for (int i = 0; i < w; i ++) { cin >> a[i]; } sort(a.begin(), a.end(), greater<>()); vector v(n + 2, vector<int>(m + 2)); auto sum = v; for (int i = 1; i + k <= n + 1; i ++) { for (int j = 1; j + k <= m + 1; j ++) { v[i][j] ++, v[i][j + k] --; v[i + k][j]--, v[i + k][j + k]++; } } vector<i64> d; for (int i = 1; i <= n; i ++) { for (int j = 1; j <= m; j ++) { v[i][j] += v[i - 1][j] + v[i][j - 1] - v[i - 1][j - 1]; d.push_back(v[i][j]); } } sort(d.begin(), d.end(), greater<>()); i64 ans = 0; for (int i = 0; i < w; i ++) { ans += d[i] * a[i]; } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
F. Color Rows and Columns
思路
一个长为 \(a\),宽为 \(b\) 的矩形可以产生的贡献应该是 \(0,1,2,\dots a+b-3,a+b-2,a+b\),诶?\(a+b-1\) 呢?试想一下,当矩形中只差一个未涂满的情况下,长和宽分别是 \(a-1,b-1\),这个时候产生的贡献为 \(a+b-2\),那么当我们填上这一个点,长和宽就会刚好被涂满,从而产生 \(a+b\) 的贡献,所以 \(a+b-1\) 的情况是不会产生的。
根据以上结论,我们可以处理出每个矩形获得相应点数的代价和贡献,然后从每个矩形中选择一个贡献(不选择以贡献为0的形式展现)出来,然后得到至少为 \(k\) 的最小代价。
没错,这就是分组背包典型模板了,值得注意的是题目中要求的是至少为 \(k\) 的最小代价,也就是说可能存在分数大于 \(k\) 且代价更小的情况,这个时候我们背包的范围就要取大一点,至少也得 \(k+1\)(不要刚好取 \(100\),\(k\) 也可以等于\(100\))。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n, k; cin >> n >> k; vector w(n + 1, vector<array<i64, 2>>()); vector<array<int, 2>> a(n + 1); for (int j = 1, x, y; j <= n; j ++) { cin >> x >> y; a[j] = {x, y}; int tx = x, ty = y; for (int i = 1, sum = 0; i <= x + y; i ++) { if (i == x + y - 1) continue; if (i == x + y) { w[j].push_back({i, x * y}); } else { sum += min(tx, ty); tx > ty ? tx-- : ty--; w[j].push_back({i, sum}); } } } const int N = 114, M = N - 10; vector<i64> dp(N, INT_MAX); dp[0] = 0; for (int i = 1; i <= n; i ++) { for (int j = M; j >= 0; j --) { for (auto &[cost , val] : w[i]) { if (j < cost) continue; dp[j] = min(dp[j], dp[j - cost] + val); } } } i64 ans = -1; for (int i = k; i <= M; i ++) { if (dp[i] != INT_MAX) { if (ans == -1) ans = dp[i]; else ans = min(ans, dp[i]); } } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
G. Call During the Journey
思路
考虑要找最晚出发的时间,这点我们可以通过二分解决。
然后考虑 Dijkstra,如果当前的时间点采用骑车的话,其花费的时间段和打电话的时间如果有交集,那我们只有两种选择,走路 或者 在原地打完电话之后骑车;若无交集,那就正常骑车即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; struct DIJ { using i64 = long long; using PII = pair<i64, i64>; vector<i64> dis; vector<vector<array<i64, 3>>> G; int n; DIJ() {} DIJ(int n_): n(n_) { G.resize(n + 1); } void add(int u, int v, int l1, int l2) { G[u].push_back({v, l1, l2}); } void dijkstra(int s, i64 tx, i64 t1, i64 t2) { dis.assign(n + 1, 1e18); priority_queue<PII> Q; dis[s] = tx; Q.push({ -dis[s], s}); while (!Q.empty()) { auto [t, u] = Q.top(); Q.pop(); t = -t; if (dis[u] < t) continue; for (auto [v, l1, l2] : G[u]) { auto now = t + l2; if (max(t, t1) < min(t + l1, t2)) { now = min(now, t2 + l1); } else { now = min(now, t + l1); } if (dis[v] > now) { dis[v] = now; Q.push({ -dis[v], v}); } } } } }; void solve() { int n, m, t[3] {}; cin >> n >> m >> t[0] >> t[1] >> t[2]; DIJ dij(n); for (int i = 0; i < m; i ++) { int u, v, l1, l2; cin >> u >> v >> l1 >> l2; dij.add(u, v, l1, l2); dij.add(v, u, l1, l2); } i64 l = 0, r = t[0], ans = -1; while (l <= r) { i64 mid = l + r >> 1; dij.dijkstra(1, mid, t[1], t[2]); if (dij.dis[n] <= t[0]) l = mid + 1, ans = mid; else r = mid - 1; } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
H. Ksyusha and the Loaded Set
思路
考虑题中所给 \(k\) 的性质,其实就是在集合中从左往右找到第一段两两相差长度至少为 \(k\) 的位置。
所以我们可以维护一个权值线段树,对于集合中出现的数,我们可以在线段树中标记为 \(1\),那么两个 \(1\) 之间相差的 \(0\) 的个数不就等于两数相差的长度吗,那我们只用维护这个区间中最大的 \(0\) 的个数即可,想到这,其实就可以发现这是个比较板的维护 \(0/1\) 串中最大连续 \(0\) 的个数线段树,对于找长度为 \(k\) 的位置,我们可以线段树上二分,因为是从左往右找,所以找完左边还得看下中间是否符合,即判断左区间后缀连续 \(0\ +\)右区间前缀连续 \(0\) 是否 \(\ge k\),最后找右区间。
每次可以维护一个 \(set\) 代表集合中的数,最后将线段树清空,减少每次新开线段树带来的时间和空间上的损耗。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; template<class Node> struct SegmentTree { #define lc u<<1 #define rc u<<1|1 const int n, N; vector<Node> tr; SegmentTree(): n(0) {} SegmentTree(int n_): n(n_), N(n * 4 + 10) { tr.reserve(N); tr.resize(N); build(1, 1, n); } void build(int u, int l, int r) { tr[u].l = l, tr[u].r = r; if (l == r) { tr[u] = {l, r, 1, 1, 1}; return ; } i64 mid = (l + r) >> 1; build(lc, l, mid); build(rc, mid + 1, r); pushup(tr[u], tr[lc], tr[rc]); }; void pushup(Node& U, Node& L, Node& R) { //上传 U.l = L.l, U.r = R.r; if (L.presum == L.r - L.l + 1) { U.presum = L.presum + R.presum; } else { U.presum = L.presum; } if (R.lastsum == R.r - R.l + 1) { U.lastsum = L.lastsum + R.lastsum; } else { U.lastsum = R.lastsum; } U.Maxsum = max({L.Maxsum, R.Maxsum, L.lastsum + R.presum}); } void modify(int u, int pos) { if (tr[u].l >= pos && tr[u].r <= pos) { tr[u].Maxsum ^= 1; tr[u].presum = tr[u].lastsum = tr[u].Maxsum; return ; } int mid = (tr[u].l + tr[u].r) >> 1; if (pos <= mid) modify(lc, pos); else modify(rc, pos); pushup(tr[u], tr[lc], tr[rc]); } int query(int u, int k) { //区查 if (tr[u].l == tr[u].r) return tr[u].l; if (tr[lc].Maxsum >= k) return query(lc, k); if (tr[lc].lastsum + tr[rc].presum >= k) { return tr[lc].r - tr[lc].lastsum + 1; } return query(rc, k); } }; struct Node { //线段树定义 i64 l, r; i64 presum, lastsum, Maxsum; }; constexpr int N = 2e6 + 10, M = N - 10; SegmentTree<Node> tr(M); void solve() { set<int> s; int n; cin >> n; for (int i = 1; i <= n; i ++) { int x; cin >> x; s.insert(x); tr.modify(1, x); } int m; cin >> m; while (m--) { char op; int x; cin >> op >> x; if (op == '+') { s.insert(x); tr.modify(1, x); } else if (op == '-') { s.erase(x); tr.modify(1, x); } else { if (x > tr.tr[1].Maxsum) { cout << M - tr.tr[1].lastsum + 1 << ' '; } else { cout << tr.query(1, x) << ' '; } } } cout << '\n'; for (auto &x : s) { tr.modify(1, x); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
本文作者:Ke_scholar
本文链接:https://www.cnblogs.com/Kescholar/p/18360076
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略