Educational Codeforces Round 183 (Rated for Div. 2)


A. Candies for Nephews

题意:求大于等于\(n\)的最小\(3\)的倍数。

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

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::cout << (n + 2) / 3 * 3 - n << "\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. Deck of Cards

题意:一副牌有\(n\)张,每次从顶拿一张或者从底拿一张,但有些操作你不确定是从哪拿的。求每张卡是一定被拿了还是一定没被拿还是不确定。

记录每个操作的数量,那么对于第\(i\)张卡,如果顶上或底下确定可以拿它,则必被拿。想要不被拿,需要顶上和底下各自加上不确定的数量都拿不到它。否则如果操作数小于\(n\)就是可能被拿。

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

using i64 = long long;

void solve() {
	int n, k;
	std::cin >> n >> k;
	std::string s;
	std::cin >> s;
	int cnt[3]{};
	for (auto & c : s) {
		++ cnt[c - '0'];
	}

	std::string ans;
	for (int i = 1; i <= n; ++ i) {
		if (cnt[0] < i && cnt[1] <= n - i) {
			if (cnt[0] + cnt[2] < i && cnt[1] + cnt[2] <= n - i) {
				ans += '+';
			} else {
				if (cnt[0] + cnt[1] + cnt[2] == n) {
					ans += '-';
				} else {
					ans += '?';
				}
			}
		} else {
			ans += '-';
		}
	}
	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;
}

C. Monocarp's String

题意:一个只包含\(ab\)的字符串,删去一个子区间使得\(ab\)数量相等,求区间最小长度。

\(a\)\(-1\)\(b\)\(1\)。那么我们需要删掉一个子区间满足其区间和为\(b\)的数量减\(a\)的数量。
可以前缀和做,用\(map\)存前面每个前缀值出现的最晚下标。

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

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::string s;
	std::cin >> s;
	std::vector<int> sum(n + 1);
	for (int i = 0; i < n; ++ i) {
		sum[i + 1] = sum[i] + (s[i] == 'a' ? -1 : 1);
	}

	if (sum[n] == 0) {
		std::cout << 0 << "\n";
		return;
	}

	int ans = n;
	std::map<int, int> mp;
	mp[0] = 0;
	for (int i = 1; i <= n; ++ i) {
		if (mp.count(sum[i] - sum[n])) {
			ans = std::min(ans, i - mp[sum[i] - sum[n]]);
		}

		mp[sum[i]] = i;
	}

	if (ans == n) {
		ans = -1;
	}
	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;
}

D. Inversion Value of a Permutation

题意:构造一个长度为\(n\)的排列,使得其恰好有\(k\)个子数组不是升序。\(n \leq 30\)

先考虑倒着排:\(n, n - 1, n - 2, ... 1\),这样有\(\frac{n(n-1)}{2}\)个不是升序的区间。然后我们选择一个长度为\(l\)的子区间翻转的话,就使得满足条件的区间减少了\(\frac{l(l-1)}{2}\)。然后我们选择一些不相交的子区间翻转,这样我们可以凑出减少\(\frac{n(n-1)}{2} - k\)。但怎样凑也是有最优方案的,可以看作背包,重量为\(l\)的物品有\(\frac{l(l-1)}{2}\)的价值。求凑\(\frac{n(n-1)}{2} - k\)价值的最小重量及其方案。
背包预处理即可。

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

using i64 = long long;

const int N = 30 * 30;

int f[N], pre[N];

void init() {
	for (int i = 1; i < N; ++ i) {
		f[i] = 1e9;
		for (int j = 2; j * (j - 1) / 2 <= i; ++ j) {
			if (f[i - j * (j - 1) / 2] + j < f[i]) {
				f[i] = f[i - j * (j - 1) / 2] + j;
				pre[i] = j;
			}
		}
	}
}

void solve() {
	int n, k;
	std::cin >> n >> k;
	std::vector<int> ans(n);
	std::ranges::iota(ans, 0);
	std::ranges::reverse(ans);
	k = n * (n - 1) / 2 - k;
	for (int i = 0; i < n && k;) {
		int len = pre[k];
		if (i + len > n) {
			break;
		}
		std::reverse(ans.begin() + i, ans.begin() + i + len);
		k -= len * (len - 1) / 2;
		i += len;
	}

	if (k) {
		std::cout << 0 << "\n";
	} else {
		for (int i = 0; i < n; ++ i) {
			std::cout << ans[i] + 1 << " \n"[i == n - 1];
		}
	}
}

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

E. Predicting Popularity

题意:一开始你有两个属性\(ac, dr\)。有\(n\)个人也有两个属性\(a_i, d_i\)。你可以一步一步吸收这些人,如果已经有\(p\)个人,那么如果一个人满足\(\max(0, a_i - ac) + \max(0, d_i - dr) \leq p\)他就会加入你。\(m\)次修改每次改一个人的\(a_i, d_i\),求可以有多少人。

\(cnt_k\)\(k = \max(0, a_i - ac) + \max(0, d_i - dr)\)的个数。记\(pre_i = \sum_{j=0}^{i-1} cnt_j\)。那么如果有\(pre_i \geq i\)则可以加入\(i\)个人。那么最大的所有\(pre_i\)都大于等于\(i\)的前缀长度就是答案。
现在考虑修改,记\(f_i = pre_i - i\)。那么最大的\(f_i\)大于等于\(0\)的前缀长度就是答案,可以用线段树维护\(f\)。修改一个人时先把他原来的贡献删掉,如果它的\(\max(0, a_i - ac) + \max(0, d_i - dr) = k\),那么\([k + 1, n + 1]\)\(f\)都要减一,\(k\)大于\(n\)就看作\(n+1\)
然后修改后同理把修改后对应的后缀都加一就行了。然后查询答案就是在线段树内二分。

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

using i64 = long long;

const int N = 5e5 + 5;

int a[N], d[N];
int f[N];

template <class Info, class Tag>
struct LazySegmentTree {
	struct Node {
		int l, r;
		Info info;
		Tag tag;
	};

	std::vector<Node> tr;
	LazySegmentTree() {}
	LazySegmentTree(int n) {
		init(n);
	}

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

	void pushdown(Node & u, const Tag & tag) {
		u.info = u.info + tag;
		u.tag = u.tag + tag;
	}

	void pushdown(int u) {
		if (tr[u].tag.exist()) {
			pushdown(tr[u << 1], tr[u].tag);
			pushdown(tr[u << 1 | 1], tr[u].tag);
			tr[u].tag.clear();
		}
	}

	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.min = f[l];
			return;
		}

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

	void modify(int l, int r, const Tag & tag, int u = 1) {
		if (l <= tr[u].l && tr[u].r <= r) {
			pushdown(tr[u], tag);
			return;
		}

		pushdown(u);
		int mid = tr[u].l + tr[u].r >> 1;
		if (l <= mid) {
			modify(l, r, tag, u << 1);
		}

		if (r > mid) {
			modify(l, r, tag, u << 1 | 1);
		}

		pushup(u);
	}

	int query(int u = 1) {
		if (tr[u].l == tr[u].r) {
			return tr[u].info.min >= 0 ? tr[u].l : 0;
		}

		pushdown(u);
		if (tr[u << 1].info.min >= 0) {
			int mid = tr[u].l + tr[u].r >> 1;
			return std::max(mid, query(u << 1 | 1));
		} else {
			return query(u << 1);
		}
	}
};

struct Info {
	int min;
};

struct Tag {
	int add = 0;
	bool exist() {
		return add != 0;
	}

	void clear() {
		add = 0;
	}
};

Info operator + (const Info & a, const Info & b) {
	Info res{};
	res.min = std::min(a.min, b.min);
	return res;
}

Info operator + (const Info & a, const Tag & b) {
	Info res{};
	res.min = a.min + b.add;
	return res;
}

Tag operator + (const Tag & a, const Tag & b) {
	Tag res{};
	res.add = a.add + b.add;
	return res;
}

void solve() {
	int ac, dr;
	std::cin >> ac >> dr;
	int n;
	std::cin >> n;
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	for (int i = 0; i < n; ++ i) {
		std::cin >> d[i];
	}

	std::vector<int> cnt(n + 3);
	for (int i = 0; i < n; ++ i) {
		int v = std::max(0, a[i] - ac) + std::max(0, d[i] - dr);
		++ cnt[std::min(n + 1, v)];
	}

	int pre = 0;
	for (int i = 0; i <= n + 2; ++ i) {
		f[i] = pre - i;
		pre += cnt[i];
	}

	LazySegmentTree<Info, Tag> tr(n + 3);
	int m;
	std::cin >> m;
	while (m -- ) {
		int k, x, y;
		std::cin >> k >> x >> y;
		-- k;
		int last = std::max(0, a[k] - ac) + std::max(0, d[k] - dr);
		last = std::min(n + 1, last);
		tr.modify(last + 1, n + 2, Tag{-1});
		a[k] = x; d[k] = y;
		int now = std::max(0, a[k] - ac) + std::max(0, d[k] - dr);
		now = std::min(n + 1, now);
		tr.modify(now + 1, n + 2, Tag{1});
		std::cout << tr.query() << "\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. Long Journey

题意:一个人从\(0\)走到\(m\)每步可以选择不动或者加一。有\(n\)个陷阱,第\(i\)个陷阱会在时刻\(t\)满足\(t \equiv i \pmod n\)时在所有\(k \times a_i + b_i\)的位置触发。不能碰到陷阱走到\(m\)需要多少步。\(n \leq 10, m \leq 10^{12}\)

记状态\(f[i][j]\)表示走到第\(i\)个格子时刻为\(j\)的最小步数,转移就是每次走下一个最小时刻\(k\)满足\(j\)等到\(k\)\(i\)个格子都是安全的,且下一步走到\(i+1\)也是安全的。显然太大了无法计算。考虑状态取下模。即\(f[i][j]\)表示第一次到\(x \equiv i \pmod {lcm(a_1, a_2, ..., a_n)}\)格,时刻为\(t \equiv j \pmod n\)的最小步数。然后猜测在这些转移里是有环的(真是猜的)。只需要找到这个环,就能计算了。

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

using i64 = long long;

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

    int L = 1;
    for(int i = 0; i < n; i++) {
        L = std::lcm(L, a[i]);
    }

    int N = L * n;

    std::vector<std::vector<int>> safe(L, std::vector<int>(n, 1));
    for(int x = 0; x < L; ++ x) {
        for(int t = 0; t < n; ++ t) {
            int i = (t - 1 + n) % n;
            if (x % a[i] == b[i]) {
                safe[x][t] = 0;
            }
        }
    }

    std::vector<int> f(N, -1);
    std::vector<i64> cost(N, 0);

    auto getNext = [&](int id) -> void {
        if (f[id] != -1) { 
            return;
        }
        int x = id / n;
        int r = id % n;
        int best = -1;
        for(int w = 0; w < n; ++ w){
            bool ok = true;
            for(int k = 1; k <= w; ++ k){
                int t2 = (r + k) % n;
                if (!safe[x][t2]) {
                    ok = false; break;
                }
            }
            if (!ok) {
                continue;
            }
            int rt = (r + w + 1) % n;
            int x2 = (x + 1 == L ? 0 : x + 1);
            if (safe[x2][rt]) {
                best = w;
                break;
            }
        }

        if (best < 0) {
            f[id] = -2;
            return;
        }
        i64 c = best + 1;
        int r2 = (r + best + 1) % n;
        int x2 = (x + 1 == L ? 0 : x + 1);
        int id2 = x2 * n + r2;
        f[id] = id2;
        cost[id] = c;
    };

    int id = 0;
    std::vector<i64> sum(N, 0);
    std::vector<i64> d(N, -1);
    i64 tot = 0;
    i64 step = 0;
    int cur = id;
    bool bad = false;
    while (step < m && d[cur] == -1) {
        d[cur] = step;
        sum[cur] = tot;
        getNext(cur);
        if (f[cur] == -2) {
            bad = true;
            break;
        }
        tot += cost[cur];
        cur = f[cur];
        step ++;
    }

    if (bad) {
        std::cout << -1 << "\n";
        return;
    }

    if (step == m) {
        std::cout << tot << "\n";
        return;
    }

    i64 cycle_len = step - d[cur];
    i64 cycle_cost = tot - sum[cur];

    i64 rem = m - d[cur];
    i64 cnt = rem / cycle_len;
    i64 ans = sum[cur] + cnt * cycle_cost;
    i64 rem2 = rem % cycle_len;

    for (i64 i = 0; i < rem2; ++ i) {
        getNext(cur);
        ans += cost[cur];
        cur = f[cur];
    }
    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-10-07 01:04  maburb  阅读(550)  评论(0)    收藏  举报