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;
}
posted @ 2024-11-03 14:07  PHarr  阅读(67)  评论(0编辑  收藏  举报