Codeforces Round 1019 (Div. 2) A ~ D

A. Common Multiple

本质上是要求数组去重之后的长度。

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a[110];
bool vis[110];

inline void Solve() {
	cin >> n, fill (vis, vis + n + 1, false);
	for (int i = 1; i <= n; i ++) 
		cin >> a[i], vis[a[i]] = true;
	int tot = 0;
	for (int i = 1; i <= n; i ++)
		tot += vis[i] ? 1 : 0;
	cout << tot << "\n";
	return;
}

signed main() {
	ios_base::sync_with_stdio (false);
	cin.tie (nullptr), cout.tie (nullptr);
	int test; cin >> test;
	while (test --)
		Solve();
	return 0;
}

B. Binary Typewriter

每次遇到相邻的两个不同的直接开始往左右扩散出极长连续 \(0/1\) 的子序列,如果整个串刚好是由一个极长连续 \(1\) 的子序列和极长连续 \(0\) 的子序列拼起来,那么只会一次更换按钮操作(一开始就统计 \(a_i \neq a_{i-1}\) 的个数),否则就可以通过翻转这个左右扩散出的极长连续 \(0/1\) 的子序列来减伤 \(2\) 次操作。

#include <bits/stdc++.h>
#define int long long
using namespace std;

int test, n, a[200010];

inline void Solve() {
	scanf ("%lld", &n);
	for (int i = 1; i <= n; i ++) scanf ("%1lld", &a[i]);
	int res = 0, ans = 0;
	for (int i = 1; i <= n; i ++) res += (a[i] != a[i - 1]);
	for (int i = 1; i <= n; i ++) {
		if (a[i] != a[i - 1]) {
			int posl = i - 1, posr = i;
			while (~posl && a[posl] == a[i - 1]) posl --;
			while (posr <= n && a[posr] == a[i]) posr ++;
			ans = max (ans, 1LL * (~posl ? 1 : 0) + 1LL * (posr != n + 1 ? 1 : 0));
		}
	}
	printf ("%lld\n", res + n - ans);
}

signed main() {
	scanf ("%lld", &test);
	while (test --) Solve();
	return 0;
}

C. Median Splits

感觉像是乱搞过得。

如果说二分求中位数,那么可以对于 \(\leq mid\) 的数赋为 \(1\)\(\geq mid\) 的数,如果说两段加和为 \(0\) 那么 \(mid\) 就是中位数。

那么我们对于这题可以考虑处理前缀后缀(实则我是赛时打表以及注意力惊人发现),满足 \(\leq k\) 的数过半且记录最小的长度,

我们记 \(pre_i = \sum\limits_{j=1}^{i}{\begin{cases}1,a_i \leq k \\ -1,a_i > k\end{cases}}\),对于 \(pre_i \geq 0\),我们查询小于等于它的第一个,如果不是它自己,那么说明存在合法方案,因为此时中间那段的中位数也一定是 \(\leq k\) 的,后缀同理。

如果最后前后缀长度和 \(< n\) 也有解,此时中间那一段可能是只有一个数。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e5 + 10, INF = 0x7f7f7f7f7f7f;
int test, n, a[MAXN], pre[MAXN], b[MAXN], K;
map<int, int> mp;

inline void Solve() {
	scanf ("%lld %lld", &n, &K);
	for (int i = 1; i <= n; i ++) {
		scanf ("%lld", &a[i]);
		b[i] = (a[i] <= K ? 1 : -1);
		pre[i] = pre[i - 1] + b[i];
	}
	int tmpX = INF, tmpY = INF;
	mp.clear();
	for (int i = 1; i <= n; i ++) {
		if (pre[i] >= 0) {
			if (tmpX == INF) tmpX = i;
			if (mp.find (pre[i]) != mp.end()) mp[pre[i]] = min (mp[pre[i]], i);
			else mp[pre[i]] = i;
			int tmp = (-- mp.upper_bound (pre[i])) -> second;
			if (tmp < i) return puts ("YES"), void();
		}
	}
	reverse (b + 1, b + n + 1);
	fill (pre + 1, pre + n + 1, 0), mp.clear();
	for (int i = 1; i <= n; i ++) {
		pre[i] = pre[i - 1] + b[i];
		if (pre[i] >= 0) {
			if (tmpY == INF) tmpY = i;
			if (mp.find (pre[i]) != mp.end()) mp[pre[i]] = min (mp[pre[i]], i);
			else mp[pre[i]] = i;
			int tmp = (-- mp.upper_bound (pre[i])) -> second;
			if (tmp < i) return puts ("YES"), void();
		}
	}
	if (tmpX + tmpY >= n) puts ("NO");
	else puts ("YES");
}

signed main() {
	scanf ("%lld", &test);
	while (test --) Solve();
	return 0;
}

D. Local Construction

因为次序最多 \(\log n\),实则可以考虑枚举每次操作然后模拟。

这里只考虑对于奇数次序的操作,因为偶数同理。

奇数的情况我们考虑局部最小值,此时剩下的 \(a_i\) 一定小于两边的 \(a_{i-1},a_{i+1}\),当然删除的可能也是一段极长连续段,考虑小到大建边跑拓扑,对于连续极长的段我们相邻两个也建边。

拓扑序就是我们想要的答案。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e5 + 10, INF = 0x7f7f7f7f;
int test, n, a[MAXN], ls[MAXN], rs[MAXN], ans[MAXN];
int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], indeg[MAXN], idx;

inline void Addedge (int u, int v) { indeg[v] ++, to[idx] = v, nxt[idx] = head[u], head[u] = idx ++; }

inline void tpSort() {
	queue<int> q;
	for (int i = 1; i <= n; i ++) if (!indeg[i]) q.push(i);
	int index = 0;
	while (!q.empty()) {
		int u = q.front(); q.pop();
		ans[u] = ++ index;
		for (int i = head[u]; ~i; i = nxt[i]) if (!--indeg[to[i]]) q.push (to[i]);
	}
	return;
}

inline void Solve() {
	cin >> n, idx = 0;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i], a[i] = (!~a[i]) ? INF : a[i];
		ls[i] = i - 1, rs[i] = i + 1;
		head[i] = -1, indeg[i] = 0;
	}
	for (int x = 1; x <= 20; x ++) {
		for (int i = 1; i <= n; i ++) {
			if (a[i] > x) {
				if (x & 1) {
					if (ls[i] >= 1 && ls[i] <= n) Addedge (i, ls[i]);
					if (rs[i] >= 1 && rs[i] <= n) Addedge (i, rs[i]);
				} else {
					if (ls[i] >= 1 && ls[i] <= n) Addedge (ls[i], i);
					if (rs[i] >= 1 && rs[i] <= n) Addedge (rs[i], i);
				}
			}
		}
		int posl = 0, posr = n + 1, st = n + 1, ed = 0;
		for (int i = 1; i <= n; i ++) if (a[i] >= x) { st = i; break; }
		for (int i = st; i <= n; i = rs[i]) if (a[i] != x) { posl = i; break; }
		for (int i = st; i < posl; i = rs[i]) {
			if (x & 1) Addedge (rs[i], i);
			else Addedge (i, rs[i]);
		}
		for (int i = n; i >= 1; i --) if (a[i] >= x) { ed = i; break; }
		for (int i = ed; i >= 1; i = ls[i]) if (a[i] != x) { posr = i; break; }
		for (int i = ed; i > posr; i = ls[i]) {
			if (x & 1) Addedge (ls[i], i);
			else Addedge (i, ls[i]);
		}
		for (int i = rs[posl]; i < posr && i; i ++) {
			if (a[i] == x && a[ls[i]] == x && a[rs[i]] == x)
				Addedge (ls[i], i), Addedge (i, rs[i]);
		}
		for (int i = 1; i <= n; i ++) {
			if (a[i] == x)
				rs[ls[i]] = rs[i], ls[rs[i]] = ls[i];
		}
	}
	tpSort();
	for (int i = 1; i <= n; i ++)
		cout << ans[i] << " \n"[i == n];
}

signed main() {
	ios_base::sync_with_stdio (false);
	cin.tie (nullptr), cout.tie (nullptr);
	cin >> test;
	while (test --)
		Solve();
	return 0;
}
posted @ 2025-04-22 20:53  xAlec  阅读(36)  评论(0)    收藏  举报