The 3rd Universal Cup. Stage 15: Chengdu

1|0A. Arrow a Row


一个简单的构造题,构造的思路是先把又侧的连续>放满,再从左侧逐个开始放,如果是>就放一个长度为 5 的,如果是-,可以一次性直接把连续的都放了。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using vi = vector<int>; using pii = pair<int,int>; string check(int n, vector<pii> op) { string s(n + 1, '*'); for(auto [x, len] : op) { s[x] = s[x + len - 1] = s[x + len - 2] = s[x + len - 3] = '>'; for(int i = x + 1; i <= x + len - 4; i ++) s[i] = '-'; cout << s << "\n"; } return s; } void solve() { string s; cin >> s; int n = s.size() - 1; if(s[0] != '>' or s[n] != '>' or s[n - 1] != '>' or s[n - 2] != '>'){ cout << "No\n"; return; } bool ok = false; for(int i = 1; i <= n - 3; i ++) { if(s[i] == '-') { ok = true; break; } } if(ok == false){ cout << "No\n"; return; } vector<pii> op; int lst = -1; for(int i = n; i >= 0; i --){ if(s[i] == '>' and s[i - 1] == '>' and s[i - 2] == '>') { op.emplace_back(i - 4, 5); i = i - 2; } else if(s[i] == '>' and s[i - 1] == '>') { op.emplace_back(i - 3, 5); lst = i - 2; break; } else if(s[i] == '>') { op.emplace_back(i - 2, 5); lst = i - 1; break; } else { lst = i; break; } } for(int i = 0; i <= lst; i ++){ if(s[i] == '>') { if(s[i + 1] == '-'){ int len = 4, j = i; while(s[i + 1] == '-') i ++, len ++; op.emplace_back(j, len); } else { op.emplace_back(i, 5); } } else { assert(false); } } cout << "Yes " << op.size() << "\n"; for(auto [x, y]: op) cout << x + 1 << " " << y << "\n"; // check(n, op); return; } int main() { ios::sync_with_stdio(false), cin.tie(nullptr); int T; cin >> T; while(T --) solve(); return 0; }

2|0B. Concave Hull


面积最大的点,一定是先求出一个凸包,然后选择凸包内的一个点,和凸包上的一条边。并把这条边向内折。

有一个容易想到的结论是,除了凸包外的点再求一个凸包。凸包内的点一定是内凸包上的点。

但是直接枚举边和点肯定是不行的。我们发现枚举外凸包上的边,在枚举内凸包上的点。关于面积的变化是单调的。因此可用双指针求解。

但是要注意,枚举内凸包点,双指针外凸包的边是不行。因为面积的变化并不单调。

#include <bits/stdc++.h> using i32 = int32_t; using i64 = long long; #define int i64 const int inf = LLONG_MAX / 2; struct Point { i64 x, y; Point(i64 x = 0, i64 y = 0) : x(x), y(y) {}; }; using Vec = Point; using Points = std::vector<Point>; Vec operator-(Vec u, Vec v) { return Vec(u.x - v.x, u.y - v.y); } int operator^(Vec u, Vec v) { return u.x * v.y - u.y * v.x; } bool operator<(Point A, Point B) { if (A.x != B.x) return A.x < B.x; return A.y < B.y; } bool check(Point p, Point q, Point r) { return 0 < ((q - p) ^ (r - q)); } std::pair<Points, Points> chull(Points &ps) { std::sort(ps.begin(), ps.end()); std::vector<int> I{0}, used(ps.size()); for (int i = 1; i < ps.size(); i++) { // 下凸包 while (I.size() > 1 and !check(ps[I[I.size() - 2]], ps[I.back()], ps[i])) used[I.back()] = 0, I.pop_back(); used[i] = 1, I.push_back(i); } for (int i = ps.size() - 2, tmp = I.size(); i >= 0; i--) { // 上凸包 if (used[i]) continue; while (I.size() > tmp and !check(ps[I[I.size() - 2]], ps[I.back()], ps[i])) used[I.back()] = 0, I.pop_back(); used[i] = 1, I.push_back(i); } Points H; I.pop_back(); for (auto i: I) H.push_back(ps[i]); Points notH; for (int i = 1; i < ps.size(); i++) { if (used[i]) continue; notH.push_back(ps[i]); } return std::pair(H, notH); } int area(Point a, Point b, Point c) { return std::abs((b - a) ^ (c - a)); } void solve() { int n; std::cin >> n; Points ps(n); for (auto &[x, y]: ps) std::cin >> x >> y; auto [c1, new_ps] = chull(ps); ps = move(new_ps); int chull_area = 0; for (int i = 0; i < c1.size(); i++) chull_area += c1[i] ^ c1[(i + 1) % c1.size()]; if (ps.empty()) { std::cout << -1 << "\n"; } else if (ps.size() < 3) { int ret = inf; for (int i = 0; i < ps.size(); i++) for (int j = 0; j < c1.size(); j++) ret = std::min(ret, area(ps[i], c1[j], c1[(j + 1) % c1.size()])); std::cout << chull_area - ret << "\n"; } else { auto [c2, _] = chull(ps); int it = 0, val = area(c1[0], c1[1], c2[0]); for (int i = 1, x; i < c2.size(); i++) { x = area(c1[0], c1[1], c2[i]); if (x < val) val = x, it = i; } int ret = val; for (int i = 1, j = (i + 1) % c1.size(); i < c1.size(); i++, j = (j + 1) % c1.size()) { val = area(c1[i], c1[j], c2[it]); for (int x; true; it = (it + 1) % c2.size()) { x = area(c1[i], c1[j], c2[(it + 1) % c2.size()]); if (x > val) break; val = x; } ret = std::min(ret, val); } std::cout << chull_area - ret << "\n"; } return; } i32 main() { i32 T; std::cin >> T; while (T--) solve(); return 0; }

3|0G. Expanding Array


这个题,我的做法就是直接暴搜,然后猛猛加优化就过了。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = uint64_t; using vi = vector<int>; using pii = pair<int,int>; const i64 P = 1e10; unordered_set<i64> vis; unordered_set<int> cnt; void dfs(int l, int r) { if(l > r) swap(l, r); if(vis.insert((i64)l * P + r).second == false) return; int mid = (l & r); cnt.insert(mid); if(l != mid and mid != r and mid != 0) dfs(l, mid), dfs(mid, r); mid = (l | r); cnt.insert(mid); if(l != mid and mid != r and mid != 0) dfs(l, mid), dfs(mid, r); mid = (l ^ r); cnt.insert(mid); if(l != mid and mid != r and mid != 0) dfs(l, mid), dfs(mid, r); return ; } int main() { ios::sync_with_stdio(false), cin.tie(nullptr); int n; cin >> n; vi a(n); for(int i = 0; i < n; i ++) cin >> a[i], cnt.insert(a[i]); cnt.insert(0); for(int i = 1; i < n; i ++) if(a[i - 1] != a[i]) dfs(a[i - 1], a[i]); cout << cnt.size(); return 0; }

4|0I. Good Partitions


首先我们求出序列中每一段连续不下降子串的长度,长度的 gcd 一定是可以的,并且 gcd 的因数也是可以的。除此之外,还有一种情况是舍去最后一段的 gcd 及其长度也是可行。

如果说我们可以求出 gcd,我们就可以O(N)的求出所有的因子。

现在我们考虑如何快速的实现修改和查询 gcd,如果a[i]>a[i+1],则说明i 是一个不下降子串的结尾。我们可以新开一个数组b[i],如果a[i]>a[i+1]b[i]=i,否则b[i]=1。此时我们找到每一个不是1的数字,以及他前面第一个不是1 的数字,我们就可以快速的求出不下降子串的长度。

这样的话,我们需要就是单点修改和区间查询,我们可以用线段实现这个操作。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int i64 using vi = std::vector<int>; struct Info { int d, li, ri; Info() { d = 0; li = ri = -1; } Info(int d, int li, int ri) : d(d), li(li), ri(ri) {} Info operator+(const Info &b) { Info res = *this; if (res.li == -1) res.li = b.li; if (b.ri != -1) res.ri = b.ri; res.d = gcd(res.d, b.d); if (ri != -1 and b.li != -1) res.d = gcd(res.d, b.li - ri); return res; } }; struct Node { i32 l, r; Info info; Node *left, *right; Node() {}; Node(int p, int x) { l = p, r = p; info = Info(0, x, x); left = right = nullptr; } Node(int l, int r, Node *left, Node *right) : l(l), r(r), left(left), right(right) { info = left->info + right->info; } }; Node *build(int l, int r, const vi &b) { if (l == r) return new Node(l, b[l]); i32 mid = (l + r) / 2; auto left = build(l, mid, b); auto right = build(mid + 1, r, b); return new Node(l, r, left, right); } void modify(int p, int x, Node *cur) { if (cur->l == p and p == cur->r) { cur->info.li = cur->info.ri = x; return; } int mid = (cur->l + cur->r) / 2; if (p <= mid) modify(p, x, cur->left); else modify(p, x, cur->right); cur->info = cur->left->info + cur->right->info; } Info query(int n, Node *cur) { if (cur->info.ri != n) return cur->info; if (cur->l == cur->r) return Info(); Info res; if (cur->left->info.ri != n) res = res + cur->left->info; else res = res + query(n, cur->left); if (cur->right->info.ri != n) res = res + cur->right->info; else res = res + query(n, cur->right); return res; } void solve() { int n, q; cin >> n >> q; vi a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; vi b(n + 1, -1); for (int i = 1; i < n; i++) { if (a[i] > a[i + 1]) { b[i] = i; } } b[0] = 0, b[n] = n; auto root = build(0, n, b); int d1 = root->info.d; int d2 = query(n, root).d; unordered_set<int> cnt; if (d2 == 0) { cout << n << "\n"; } else { for (int i = 1; i * i <= d1; i++) { if (d1 % i != 0) continue; cnt.insert(i); if (d1 / i != i) cnt.insert(d1 / i); } for (int i = 1; i * i <= d2 and d1 != d2; i++) { if (d2 % i != 0) continue; cnt.insert(i); if (d2 / i != i) cnt.insert(d2 / i); } cout << cnt.size() << "\n"; } for (int idx, val, ok; q; q--) { cin >> idx >> val; a[idx] = val, ok = 1; if (idx - 1 >= 1 and a[idx - 1] > a[idx]) { if (b[idx - 1] != idx - 1) { b[idx - 1] = idx - 1; modify(idx - 1, idx - 1, root); ok = 0; } } else if (idx - 1 >= 1) { if (b[idx - 1] != -1) { b[idx - 1] = -1; modify(idx - 1, -1, root); ok = 0; } } if (idx + 1 <= n and a[idx] > a[idx + 1]) { if (b[idx] != idx) { b[idx] = idx; modify(idx, idx, root); ok = 0; } } else if (idx + 1 <= n) { if (b[idx] != -1) { b[idx] = -1; modify(idx, -1, root); ok = 0; } } if (ok) { if (d2 == 0) cout << n << "\n"; else cout << cnt.size() << "\n"; continue; } d1 = root->info.d; d2 = query(n, root).d; cnt.clear(); if (d2 == 0) { cout << n << "\n"; } else { for (int i = 1; i * i <= d1; i++) { if (d1 % i != 0) continue; cnt.insert(i); if (d1 / i != i) cnt.insert(d1 / i); } for (int i = 1; i * i <= d2 and d1 != d2; i++) { if (d2 % i != 0) continue; cnt.insert(i); if (d2 / i != i) cnt.insert(d2 / i); } cout << cnt.size() << "\n"; } } return; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int T; cin >> T; while (T--) solve(); return 0; }

我们考虑前两个分界点p1,p2。第一段的长度就是p1,第二段的长度是p2p1。因此有gcd(p1,p2p1)=gcd(p1,p2)。我们可以依次类推,发现并不需要维护每一个分界点前一个分界点,而是直接计算所有分界点的gcd即可。

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int i64 using vi = std::vector<int>; const int N = 2e5; vi cntp; struct Info { int d; Info() { d = 0; } Info(int d) : d(d) {} Info operator+(const Info &b) { Info res = *this; res.d = gcd(res.d, b.d); return res; } }; struct Node { i32 l, r; Info info; Node *left, *right; Node() {}; Node(int p, int x) { l = p, r = p; info = Info(x); left = right = nullptr; } Node(int l, int r, Node *left, Node *right) : l(l), r(r), left(left), right(right) { info = left->info + right->info; } }; Node *build(int l, int r, const vi &b) { if (l == r) return new Node(l, b[l]); i32 mid = (l + r) / 2; auto left = build(l, mid, b); auto right = build(mid + 1, r, b); return new Node(l, r, left, right); } void modify(int p, int x, Node *cur) { if (cur->l == p and p == cur->r) { cur -> info.d = x; return; } int mid = (cur->l + cur->r) / 2; if (p <= mid) modify(p, x, cur->left); else modify(p, x, cur->right); cur->info = cur->left->info + cur->right->info; } void solve() { int n, q; cin >> n >> q; vi a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; vi b(n + 1, 0); for (int i = 1; i < n; i++) { if (a[i] > a[i + 1]) { b[i] = i; } } b[0] = 0; auto root = build(0, n, b); if (root->info.d == 0) { cout << n << "\n"; } else { cout << cntp[root->info.d] << "\n"; } for (int idx, val; q; q--) { cin >> idx >> val; a[idx] = val; if (idx - 1 >= 1 and a[idx - 1] > a[idx]) { if (b[idx - 1] != idx - 1) { b[idx - 1] = idx - 1; modify(idx - 1, idx - 1, root); } } else if (idx - 1 >= 1) { if (b[idx - 1] != -1) { b[idx - 1] = -1; modify(idx - 1, 0, root); } } if (idx + 1 <= n and a[idx] > a[idx + 1]) { if (b[idx] != idx) { b[idx] = idx; modify(idx, idx, root); } } else if (idx + 1 <= n) { if (b[idx] != -1) { b[idx] = -1; modify(idx, 0, root); } } if (root->info.d == 0) { cout << n << "\n"; } else { cout << cntp[root->info.d] << "\n"; } } return; } void init() { cntp = vi(N + 1, 1); for (int i = 2; i <= N; i++) for (int j = i; j <= N; j += i) cntp[j]++; return; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); init(); int T; cin >> T; while (T--) solve(); return 0; }

5|0J. Grand Prix of Ballance


读题加模拟就做完了

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; #define int i64 using vi = vector<int>; using pii = pair<int,int>; void solve() { int n, m, q; cin >> n >> m >> q; vector<vi> finsh(n + 1); vector<set<int>> vist(n + 1); int now_Level = -1; for(int op, id, x; q; q --) { cin >> op; if(op == 1) { cin >> x; now_Level = x; } else if(op == 2) { cin >> id >> x; if(x != now_Level) continue; if(vist[x].insert(id).second == false) continue; if(x == now_Level) finsh[x].push_back(id); } else { cin >> id >> x; if(x != now_Level) continue; vist[x].insert(id); } } vi points(m + 1); for(int i = 1; i <= n; i ++) { for(int v = m; auto j : finsh[i]) points[j] += v, v --; } vi rank(m); iota(rank.begin(), rank.end(), 1); ranges::sort(rank, [&](int x, int y) -> bool { if(points[x] != points[y]) return points[x] > points[y]; return x < y; }); for(auto i : rank) cout << i << " " << points[i] << "\n"; } i32 main() { ios::sync_with_stdio(false), cin.tie(nullptr); int T; cin >> T; while(T --) solve(); return 0; }

6|0L. Recover Statistics


签到题

#include <bits/stdc++.h> using namespace std; using i32 = int32_t; using i64 = long long; using vi = std::vector<int>; int main() { ios::sync_with_stdio(false), cin.tie(nullptr); i64 P50, P95, P99; cin >> P50 >> P95 >> P99; cout << 100 << "\n"; for(int i = 1; i <= 50; i ++) cout << P50 << " "; for(int i = 1; i <= 45; i ++) cout << P95 << " "; for(int i = 1; i <= 4; i ++) cout << P99 << " "; cout << P99 + 1 << "\n"; return 0; }

__EOF__

本文作者PHarr
本文链接https://www.cnblogs.com/PHarr/p/18523207.html
关于博主:前OIer,SMUer
版权声明CC BY-NC 4.0
声援博主:如果这篇文章对您有帮助,不妨给我点个赞
posted @   PHarr  阅读(90)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
历史上的今天:
2021-11-03 AtCoder Beginner Contest 169
点击右上角即可分享
微信分享提示