Codeforces Round 925 (Div. 3)

ABC 都是一眼题,用了 16 min。

D 没有一眼看出来,先往后看。E 是博弈论直接跳过。F 一眼出思路。G 一开始都错题了,不会做。遂先写 F。

发现我不会判断图中是否有环。一开始写了个 dfs 以为能过结果复杂度是 \(\mathcal O(n^2)\) 的,罚时 +1。想改成 DP 那样的记忆化发现很假。于是bdfs拓扑排序,写了两个不同的版本,第一个假了,第二个终于过了。 现在已经 67 min 了。

再回来看 D,第二眼会了一个比较麻烦的做法。直接开始写,过时到了 90 min。

然后想做 G。愚蠢的写了个五维 DP \(f_{a, b, c, d, p}\) 表示四种拼图分别由 \(a, b, c, d\) 张,最后一张是 \(p\) 的方案数。但是复杂度是 \(\mathcal O(abcd)\) 的,一开始算成了 \(\mathcal O(a + b + c + d)\)。样例四一直 T,我以为是常数问题,于是在愚蠢地卡常。最后反应过来复杂度不对就放弃了。

E 是博弈论,压根没思路。

A. Recovering a Small String

一个长度为 \(3\) 的由小写字母组成的字符串 \(s\) 的价值被定义为它的每一个字母在字母表中的位置之和。例如 \(\texttt{cat}\) 的价值为 \(3 + 1 + 20 = 24\)

给定 \(n\),求字典序最小的价值为 \(n\) 的字符串。

多测。\(1 \le t \le 100\)\(3 \le n \le 78\)

直接枚举 \(s\) 的每一位是哪个字符。我们希望让字典序尽量小,所以我们每一位都 \(\texttt a \sim \texttt z\) 枚举,第一个价值为 \(n\) 的字符串即为答案。

void solve() {
	int n = read();
	fup (i, 'a', 'z')
		fup (j, 'a', 'z')
			fup (k, 'a', 'z') {
				if (i - 'a' + 1 + j - 'a' + 1 + k - 'a' + 1 == n) {
					putchar(i), putchar(j), putchar(k);
					puts("");
					return;
				}
			}
}

B. Make Equal

\(n\) 个装着水的容器,第 \(i\) 个容器中有 \(a_i\) 单位的水。保证 \(n \mid \sum a_i\)

你可以进行若干次操作。每次操作你可以从容器 \(i\) 倒入容器 \(j\) 任意数量的水,但是需要满足 \(i < j\)

问最后能否将所有容器中的水量相同。

多测。\(1 \le t \le 10^4\)\(\sum n \le 2 \times 10^5\)\(0 \le a_i \le 10^9\)

模拟。

首先求出最终每个容器中的水量,即 \(s = \dfrac 1n\sum a_i\)

然后从前往后枚举每个容器 \(i\),并记录 \(x\) 表示 \(i\) 之前的容器在留下 \(s\) 单位后剩余的总水量。分类讨论:

  • 如果 \(a_i \ge s\):直接将 \(a_i\) 中多余的水倒掉,并将 \(x \gets x + a_i - s\)
  • 如果 \(a_i < s\):首先计算出我需要补充多少水 \(y = s - a_i\),那么:
    • 如果 \(y \le x\):直接将前面剩下的水倒入容器 \(i\) 中,并将 \(x \gets x - y\)
    • 如果 \(y > x\):答案为 NO,结束即可。

最后如果都能填完输出 YES。

void solve() {
	int n = read(), s = 0;
	vector<int> a(n);
	for (int &t : a) t = read(), s += t;
	s /= n;
	int x = 0;
	for (int &t : a) {
		if (t >= s) x += t - s;
		else {
			int y = s - t;
			if (y > x) return puts("NO"), void();
			x -= y;
		}
	}
	puts("YES");
}

C. Make Equal Again

给定一个长度为 \(n\) 的数组 \(a\)

你可以进行至多一次操作。你可以选择三个整数 \(i, j, x(1 \le i \le j \le n)\) 并将 \(a_i \sim a_j\) 都替换为 \(x\),代价为 \(j - i + 1\)

求如果要将 \(a\) 数组中的所有元素变得相同的最小代价。

多测。\(1 \le t \le 10^4\)\(\sum n \le 2 \times 10^5\)\(1 \le a_i \le n\)

首先分别算出从头和从尾最多有多少元素保持相同。即计算最大的 \(x, y\) 使得 \(a_x = a_{x - 1} = \dots = a_1\)\(a_{n - y + 1} = a_{n - y +2} = \dots = a_n\)

如果 \(a_1 \ne a_n\),则答案为 \(\min(n - x, n - y)\)。否则答案为 \(n - x - y\)

但是需要先特判是否最初 \(a\) 中的元素相同。若是输出 \(0\)

int solve() {
	int n = read();
	vector<int> a(n);
	for (int &t : a) t = read();
	
	if (*max_element(a.begin(), a.end()) == *min_element(a.begin(), a.end())) return 0;
	
	int res, x, y;
	fup (i, 1, n - 1) {
		if (a[i] != a[i - 1]) {
			x = i;
			break;
		}
	}
	
	fdw (i, n - 2, 0) {
		if (a[i] != a[i + 1]) {
			y = n - 1 - i;
			break;
		}
	}
	
	res = min(n - x, n - y);
	
	if (a[0] == a[n - 1]) {
		res = n - x - y;
	}
	
	return res;
}

D. Divisible Pairs

给定一个长度为 \(n\) 的数组 \(a\) 和两个整数 \(x, y\)

你需要找有多少数对 \(i, j(1 \le i < j \le n)\) 满足 \(x \mid (a_i + a_j)\)\(y \mid (a_i - a_j)\)

多测。\(1 \le t \le 10^4\)\(\sum n \le 2 \times 10^5\)\(1 \le x, y, a_i \le 10^9\)

首先处理第二个条件 \(y \mid (a_i - a_j)\)。不难发现如果这个条件满足就意味着 \(a_i \equiv a_j \pmod y\)

所以我们可以用 map + vector 存储每个除以 \(y\) 的余数出现的位置。

map<int, vector<int> > v;
for (int i = 1; i <= n; ++ i ) {
    scanf("%d", a + i);
    v[a[i] % y].push_back(i);
}

然后我们需要在每个 vector 中找整数 \(i, j(i < j)\) 使得 \((a_i + a_j) \bmod x = 0\),即 \(a_i \bmod x + a_j \bmod x \equiv 0 \pmod x\)

这仍然启发我们用 map 维护一个桶,表示对于每一个除以 \(x\) 的余数出现了多少次。

然后我们枚举上述 \(i\),那么 \(j\) 的个数即满足 \(a_i + a_j \equiv 0 \pmod x\)\(j\) 的个数,即满足 \(a_j \equiv x - a_i \pmod x\)\(j\) 的个数。然后用最开始维护的桶计算即可。

注意这样计算我们并没有满足 \(i < j\) 这个条件,但是不难发现我们算出的答案是最终答案的两倍。

最终时间复杂度相当于是每个 vector 的大小之和,即 \(n\)。加上 STL 总时间复杂度为 \(\mathcal O(n \log n)\)

int T, n, a[N], x, y;
map<int, vector<int> > v;
map<int, int> p;
 
LL solve() {
	scanf("%d%d%d", &n, &x, &y);
	v.clear();
	for (int i = 1; i <= n; ++ i ) {
		scanf("%d", a + i);
		v[a[i] % y].push_back(i);
	}
	
	LL res = 0;
	for (auto t : v) {
		int i = t.fi;
		p.clear();
		for (int &l : t.se) {
			++ p[a[l] % x];
		}
		
		for (auto o : p) {
			int l = o.fi;
				int r = l ? x - l : 0;
				if (l == r) res += (LL)p[l] * (p[l] - 1);		// 注意判断相同的情况
				else res += (LL)p[l] * p[r];
			}
	}
	
	return res / 2;
}

E. Anna and the Valentine's Day Gift

Anna 和 Sasha 在玩游戏,他们有一个长度为 \(n\) 的数组 \(a\)

Anna 为先手。两人分别操作:

  • 轮到 Anna 时,她首先需要选择一个 \(a_i\),并将其按照数位翻转,并省略前导零。例如 \(42\) 翻转为 \(24\)\(1580\) 翻转为 \(851\)。这样操作后,\(a\) 数组大小不变。
  • 轮到 Sasha 时,他需要选择 \(a_i, a_j(i \ne j)\),并以任意顺序将其连接。例如 \(2007\)\(19\) 可以连接为 \(200719\)\(192007\)。这样操作后,\(a\) 数组大小减一。

玩家不能跳过回合。当 \(a\) 中只剩下一个整数时,如果它大于等于 \(10^m\),Sasha 获胜。否则 Anna 获胜。

如果 Anna 和 Sasha 都以最优方式操作,谁会获胜?

多测。\(1 \le t \le 10^4\)\(\sum n \le 2 \times 10^5\)\(0 \le m \le 2 \times 10^9\)

F. Chat Screenshots

\(n\) 个人在排队,编号 \(1 \sim n\)。他们已经按照一定的顺序排好队了。

其中的 \(k\) 个人发了 \(k\) 条消息,表示他看到的所有人的排队顺序。但是每个人发的消息中都把自己放在了最前面。

请你判断这 \(k\) 个人是否都说的是真话。

多测。\(1 \le t \le 10^4\)\(1 \le k \le n \le 2 \times 10^5\)\(\sum n\cdot k \le 2 \times 10^5\)

由于每条消息的发送者都会把自己排在最前面,所以所有的 \(a_{i, 1}\) 都是没有用的。我们推理需要用的信息为 \(a_{i, 2}, a_{i, 3}, \dots, a_{i, n}\)

这些信息意味着 \(a_{i, 2}\) 一定在 \(a_{i, 3}\) 之前,\(a_{i, 3}\) 一定在 \(a_{i, 4}\) 之前,以此类推,\(a_{i, k - 1}\) 一定在 \(a_{i, k}\) 之前。

对于这种两者之间有关系的推理,可以启发我们将其转化成图论问题。

若我们将 \(a_{i, j} \to a_{i, j + 1}(2 \le j < k)\) 连边,表示 \(a_{i, j}\)\(a_{i, j + 1}\) 之前。那么如果最终的图中有环,就代表答案为 NO。反之为 YES。

判断图中是否有环可以使用拓扑排序。若我们用拓扑排序的方法遍历完整张图后,存在节点没有遍历到,那么图中有环。否则无环。

void solve() {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= k; ++ i ) {
		int x, y;
		scanf("%d", &x);
		for (int j = 2; j <= n; ++ j ) {
			scanf("%d", &y);
			if (j != 2) g[x].push_back(y);
			x = y;
		}
	}
	
	for (int i = 1; i <= n; ++ i ) {
		sort(g[i].begin(), g[i].end());
		g[i].erase(unique(g[i].begin(), g[i].end()), g[i].end());
		for (int j : g[i]) ++ d[j];
	}
	
	int cnt = 0;
	queue<int> q;
	for (int i = 1; i <= n; ++ i )
		if (!d[i])
			q.push(i),
			st[i] = true;
	
	while (q.size()) {
		int t = q.front();
		q.pop();
		for (int v : g[t]) {
			if (!st[v]) {
				if (!( -- d[v])) {
					st[v] = true;
					q.push(v);
				}
			}
		}
	}
	
	for (int i = 1; i <= n; ++ i ) cnt += st[i];
	puts(cnt == n ? "Yes" : "No");
}

G. One-Dimensional Puzzle

\(4\) 种拼图,其中第 \(i\) 种拼图有 \(c_i\) 张。

两张拼图可以连结当且仅当它们相邻的卡槽中一个凹陷一个突出。

我们希望将所有的拼图从左往右拼起来,求总方案数。答案对 \(998244353\) 取模。

多测。\(1 \le t \le 2 \times 10^5\)\(0 \le c_i \le 10^6\)\(\sum (c_1+c_2+c_3+c_4) \le 4 \times 10^6\)

posted @ 2024-02-14 17:06  2huk  阅读(43)  评论(0编辑  收藏  举报