The 3rd Universal Cup. Stage 15: Chengdu
A. 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;
}
B. 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;
}
G. 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;
}
I. Good Partitions
首先我们求出序列中每一段连续不下降子串的长度,长度的 gcd 一定是可以的,并且 gcd 的因数也是可以的。除此之外,还有一种情况是舍去最后一段的 gcd 及其长度也是可行。
如果说我们可以求出 gcd,我们就可以\(O(\sqrt 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;
}
我们考虑前两个分界点\(p_1,p_2\)。第一段的长度就是\(p1\),第二段的长度是\(p_2-p_1\)。因此有\(\gcd(p_1,p_2-p_1) = \gcd(p_1,p_2)\)。我们可以依次类推,发现并不需要维护每一个分界点前一个分界点,而是直接计算所有分界点的\(\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;
}
J. 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;
}
L. 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;
}