Japan Registry Services (JPRS) Programming Contest 2025#2 (AtCoder Beginner Contest 415)


A - Unsupported Type

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >>  n;
	std::set<int> s;
	for (int i =0 ; i < n; ++ i) {
		int x;
		std::cin >> x;
		s.insert(x);
	}

	int x;
	std::cin >> x;
	if (s.count(x)) {
		std::cout << "Yes\n";
	} else {
		std::cout << "No\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

B - Pick Two

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	std::string s;
	std::cin >> s;
	std::vector<int> ans;
	for (int i = 0; i < s.size(); ++ i) {
		if (s[i] == '#') {
			ans.push_back(i + 1);
		}
	}

	for (int i = 0; i < ans.size(); i += 2) {
		std::cout << ans[i] << "," << ans[i + 1] << "\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

C - Mixture

题意:有\(n\)种物品,你每次选一个物品加入集合。给出有哪些状态是非法的,求能不能不经过非法状态拿完所有物品。

\(0\)状态出发,如果一个状态\(i\)是合法,那么就枚举接下来加入\(j\),如果\(i | (1 << j)\)是合法的,就让\(st[i | (1 << j)] |= st[i]\)。则最后\(st[(1 << n) - 1] = 1\)则有解。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::string s;
	std::cin >> s;
	s = "0" + s;
	std::vector<int> st(1 << n);
	st[0] = 1;
	for (int i = 0; i < 1 << n; ++ i) {
		if (s[i] == '0') {
			for (int j = 0; j < n; ++ j) {
				if (!(i >> j & 1) && s[i + (1 << j)] == '0') {
					st[i + (1 << j)] |= st[i];
				}
			}
		}
	}

	if (st[(1 << n) - 1] > 0) {
		std::cout << "Yes\n";
	} else {
		std::cout << "No\n";
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

D - Get Many Stickers

题意:有\(n\)元,\(m\)个物品,每个物品花费\(a_i\)然后获得\(b_i\)。求最多买多少物品。

\(d_i = a_i - b_i\),那么我们选\(d_i\)小的更优。如果有\(d_j > d_i\)应该优先拿,如果\(a_j > a_i\),显然\(i\)\(j\)优,否则\(a_j < a_i\),那么先拿\(i\)有机会拿\(j\),反之不能,而且损耗更大。
排序后模拟,能拿就拿。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	i64 n;
	int m;
	std::cin >> n >> m;
	std::vector<std::tuple<i64, i64, i64>> a(m);
	for (int i = 0; i < m; ++ i) {
		i64 x, y;
		std::cin >> x >> y;
		a[i] = {x - y, x, y};
	}

	std::ranges::sort(a);
	i64 ans = 0;
	for (auto & [d, x, y] : a) {
		i64 cnt = (std::max(0ll, n - (x - 1)) + d - 1) / d;
		n -= d * cnt;
		ans += cnt;
	}
	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

E - Hungry Takahashi

题意:从第一步在\((1, 1)\)出发,要到\((n, m)\)。第\(k\)步走到\((i, j)\)获得\(a[i][j]\)减少\(p_k\)。设初始值为\(x\),那么需要找一条路径使得路径上的所有路径和都大于等于\(0\)。求最小的\(x\)

走到\((i, j)\)的步数是固定的,为\(i - j - 1\)。那么给每个\(a[i][j]\)减去\(p_{i-j-1}\)
问题就变的经典了。考虑二分\(x\),然后记\(f[i][j]\)表示到\((i, j)\)的最大值,注意如果\(f[i][j] < 0\)不可转移给\(f[i + 1][j], f[i][j + 1]\)

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n, m;
	std::cin >> n >> m;
	std::vector a(n + 1, std::vector<i64>(m + 1));
	for (int i = 1; i <= n; ++ i) {
		for (int j = 1; j <= m; ++ j) {
			std::cin >> a[i][j];
		}
	}

	std::vector<i64> h(n + m);
	for (int i = 1; i <= n + m - 1; ++ i) {
		std::cin >> h[i];
	}

	for (int i = 1; i <= n; ++ i) {
		for (int j = 1; j <= m; ++ j) {
			a[i][j] -= h[i - 1 + j - 1 + 1];
		}
	}

	const i64 inf = 1e16;

	auto check = [&](i64 x) -> bool {
		std::vector f(n + 1, std::vector<i64>(m + 1, -inf));
		for (int i = 1; i <= n; ++ i) {
			for (int j = 1; j <= m; ++ j) {
				if (i == 1 && j == 1) {
					f[i][j] = x + a[i][j];
				} else {
					f[i][j] = std::max(f[i - 1][j], f[i][j - 1]) + a[i][j];
				}

				if (f[i][j] < 0) {
					f[i][j] = -inf;
				}
			}
		}

		return f[n][m] >= 0;
	};

	i64 l = 0, r = inf;
	while (l < r) {
		i64 mid = l + r >> 1ll;
		if (check(mid)) {
			r = mid;
		} else {
			l = mid + 1;
		}
	}

	std::cout << l << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

F - Max Combo

题意:一个字符串的值为相同字符的区间的最大长度。给你一个\(s\),两种操作,一种是修改一个位置的字符,一种是询问一个子区间的价值。

考虑线段树维护区间信息,我们需要维护\(l, r, pre, suf, max\)分别表示这个区间的左右边界,最长前缀相同区间,最长后缀相同区间,最长相同区间。那么区间合并是,根据左右区间相邻端点字符是否相同进行讨论,具体看代码。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;

std::string s;

template <class Info>
struct SegmentTree {
	struct Node {
		int l, r;
		Info info;
	};

	std::vector<Node> tr;

	SegmentTree() {};
	SegmentTree(int n) {
		init(n);
	}

	SegmentTree(std::vector<Info> & info) {
		init(info);
	}

	void init(int n) {
		tr.assign(n << 2, {});
		build(0, n - 1);
	}

	void init(std::vector<Info> & info) {
		int n = info.size();
		tr.assign(n << 2, {});
		std::function<void(int, int, int)> build = [&](int l, int r, int u) -> void {
			tr[u] = {l, r, {}};
			if (l == r) {
				tr[u].info = info[l];
				return;
			}

			int mid = l + r >> 1;
			build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
			pushup(u);
		};

		build(0, n - 1, 1);
	}

	void pushup(int u) {
		tr[u].info = tr[u << 1].info + tr[u << 1 | 1].info;
	}

	void build(int l, int r, int u = 1) {
		tr[u] = {l, r, {}};
		if (l == r) {
			tr[u].info.init(l, r);
			return;
		}

		int mid = l + r >> 1;
		build(l, mid, u << 1); build(mid + 1, r, u << 1 | 1);
		pushup(u);
	}

	void modify(int p) {
		int u = 1;
		while (tr[u].l != tr[u].r) {
			int mid = tr[u].l + tr[u].r >> 1;
			if (p <= mid) {
				u = u << 1;
			} else {
				u = u << 1 | 1;
			}
		}

		tr[u].info.init(tr[u].l, tr[u].r);

		u >>= 1;
		while (u) {
			pushup(u);
			u >>= 1;
		}
	}

	Info query(int l, int r, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			return tr[u].info;
		}

		int mid = tr[u].l + tr[u].r >> 1;
		if (r <= mid) {
			return query(l, r, u << 1);
		} else if (l > mid) {
			return query(l, r, u << 1 | 1);
		}

		return query(l, r, u << 1) + query(l, r, u << 1 | 1);
	}
};

struct Info {
	int l, r;
	int pre, suf, max;
	void init(int _l, int _r) {
		l = _l;
		r = _r;
		pre = suf = max = 1;
	}
};

Info operator + (const Info & l, const Info & r) {
	Info res{};
	res.l = l.l;
	res.r = r.r;
	res.max = std::max(l.max, r.max);
	res.pre = l.pre; 
	res.suf = r.suf;
	if (s[l.r] == s[r.l]) {
		res.max = std::max(res.max, l.suf + r.pre);
		if (l.pre == l.r - l.l + 1) {
			res.pre = l.pre + r.pre;
		}

		if (r.suf == r.r - r.l + 1) {
			res.suf = l.suf + r.suf;
		}
	}
	res.max = std::max({res.max, res.pre, res.suf});
	return res;
}

void solve() {
	int n, q;
	std::cin >> n >> q;
	std::cin >> s;
	SegmentTree<Info> tr(n);
	while (q -- ) {
		int op;
		std::cin >> op;
		if (op == 1) {
			int i;
			char x;
			std::cin >> i >> x;
			-- i;
			s[i] = x;
			tr.modify(i);
		} else {
			int l, r;
			std::cin >> l >> r;
			-- l, -- r;
			std::cout << tr.query(l, r).max << "\n";
		}
	}
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}

G - Get Many Cola

题意:和\(d\)差不多,不过改为花费最多的钱。同时\(1\leq b_i < a_i \leq 300, n \leq 10^{15}, m \leq 2\times 10^5\)

考虑背包,\(f[i]\)表示有\(i\)元时的最大花费,那么背包有\(f[i - j + b_j] = \max_{j=1}^{i}(f[i - j + b_j], f[i] + b_j)\)。但背包体积太大,这时有一个\(trick\)是设置一个阈值\(K\),表示我们需要把\(n\)降到这个阈值一下才\(dp\),多余的我们贪心取。
如果贪心取\(i\),记\(d = a_i - b_i\),那么可以花费\(\lfloor \frac{n-K}{d} \rfloor \times b_i\),取最大的这个就行。

点击查看代码
#include <bits/stdc++.h>

using i64 = long long;
using i128 = __int128;

void solve() {
	i64 n;
	int m;
	std::cin >> n >> m;
	const int N = 300;
	const i64 inf = 1e16;
	std::vector<i64> A(N + 1, -inf);
	for (int i = 0; i < m; ++ i ){
		int a, b;
		std::cin >> a >> b;
		A[a] = std::max<i64>(A[a], b);
	}

	i64 B = 0, d = inf;
	const int K = 100000;
	for (int i = 1; i <= N; ++ i) {
		if (A[i] < 0) {
			continue;
		}
		//(n - k) / d * B >= (n - k) / d1 * B1
		if (B == 0 || ((i128)n * A[i] * d > (i128)n * B * (i - A[i]))) {
			B = A[i];
			d = i - A[i];
		}
	}

	i64 ans = n;
	if (n > K) {
		i64 cnt = (n - K + d - 1) / d;
		ans += cnt * B;
		n -= cnt * d;
	}

	int k = n;
	std::vector<i64> f(k + 1, -inf);
	f[k] = 0;
	for (int i = k; i >= 1; -- i) {
		for (int j = 1; j <= N && j <= i; ++ j) {
			if (A[j] > 0) {
				f[i - j + A[j]] = std::max(f[i - j + A[j]], f[i] + A[j]);
			}
		}
	}

	ans += *std::max_element(f.begin(), f.end());
	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	int t = 1;
	// std::cin >> t;
	while (t -- ) {
		solve();
	}
	return 0;
}
posted @ 2025-07-19 22:10  maburb  阅读(222)  评论(0)    收藏  举报