目录

AtCoder Beginner Contest 003

A

题意

输入一个 \(N\) ,回答 \(\frac{10000\sum_{i = 1}^{N} i}{N}\) 。

题解

实际上是回答 \(10000 \frac{\binom{N + 1}{2}}{N} = 10000 \frac{\frac{N(N + 1)}{2}}{N} = 5000 (N + 1)\) 。

	i64 n; std::cin >> n;
	std::cout << (n + 1) * 5000 << "\n";

B

题意

给定任意两个字符串,由小写字母或 '@' 组成。其中 '@' 可以被替换成 "atcoder" 中任意一个字符。询问是否可以通过替换,使两个字符串相等。

题解

设两个字符串 \(a, b\) 。

  1. 若 \(a, b\) 长度不相等,则一定无法相等。
  2. 否则 \(a, b\) 可以相等当前仅当:
    • 所有 \(x\) 满足 \(a_x\) 或 \(b_x\) 为 '@',另一个字符串的的对应字符可以在 "atcoder" 中找到。
    • 所有 \(y\) 满足 \(a_y\) 且 \(b_y\) 不是 '@' 的位置一定相同。
view
	std::string s[2];
	std::cin >> s[0] >> s[1];
	if (s[0].size() != s[1].size()) std::cout << "You will lose\n";
	else {
		int ok = 1;
		std::string t ="atcoder@";
		for (int i = 0; i < s[0].size(); i++) {
			if (s[0][i] == '@' || s[1][i] == '@') {
				int d = 0;
				if (s[d][i] != '@') d ^= 1;
				ok &= (t.find(s[d ^ 1][i]) != std::string::npos);
			}
			else {
				ok &= s[0][i] == s[1][i];
			}
		}
		std::cout << (ok ? "You can win" : "You will lose") << "\n";
	}

C

题意

给定一个长度为 \(N\) 的序列 \(R_1, R_2, \cdots, R_N\) 。初始时 \(C = 0\) ,从中任选一个数 \(R\) 可以使 \(C = \frac{C + R}{2}\) 。
可以从序列中按任意顺序选择 \(K(K \leq N)\) 个不同位置的数,询问最终能够让 \(C\) 达到的最大值。
需要相对误差或绝对误差 \(\leq 10^{-6}\) 。

题解

假设选出的 \(K\) 个数按顺序排列是 \(a_1, a_2, \cdots, a_N\) 。
则有

\[\begin{aligned} C &= \frac{\frac{\frac{0 + a_1}{2} + a_2}{2} \cdots + a_K}{2} \\ &= \frac{0 + a_1 + 2 a_2 + 4 a_3 + \cdots + 2^{K - 1} a_K}{2^{K}} \\ &= \frac{\sum_{i = 1}^{K} 2^{i - 1} a_i}{2^{K}} \end{aligned} \]

不难注意到我们要从 \(R_1, R_2, \cdots, R_N\) 中选择最大的 \(K\) 个数然后递增排序。
于是贪心选择 。。。。。。

然后写的时候我像个憨批,这个公式只能发现并证明贪心的性质。而直接实现 \(\frac{\sum_{i = 1}^{K} 2^{i - 1} a_i}{2^{K}}\) 会导致标准数据类型顶不住,且复杂度确实是 \(O(N)\) 没有更优。

逐步用 \(O(N)\) 实现 \(\frac{\frac{\frac{0 + a_1}{2} + a_2}{2} \cdots + a_K}{2}\) ,可以使中间数据不会过大。

view
	int N, K; std::cin >> N >> K;
	std::vector<int> R(N + 1);
	for (int i = 1; i <= N; i++) {
		std::cin >> R[i];
	}
	std::sort(R.begin() + 1, R.end(), std::greater<int>());
	std::vector<int> a{0};
	for (int i = 1; i <= K; i++) {
		a.push_back(R[i]);
	}
	std::reverse(a.begin() + 1, a.end());
	long double ans = 0;
	for (int i = 1; i <= K; i++) {
		ans = (ans + a[i]) / 2;
	}
	std::cout << std::fixed << std::setprecision(10) << ans << "\n";

D

diffculty 2033

题意

给一个 \(R \times C\) 的矩形,染 \(L\) 个红色和 \(D\) 个蓝色,询问有多少种方案使得包含所有颜色的极小矩形是 \(X \times Y\) 。

题解

注意这个极小。

考虑 \(X \times Y\) 矩形的可能位置,然后计算在其内的染色方案数。
总共有 \((R - X + 1)(C - Y + 1)\) 种可能的位置。

于是染色方案数为:

\[\binom{(R - X + 1)(C - Y + 1)}{1} \binom{XY}{L}\binom{XY - L}{D} = (R - X + 1)(C - Y + 1) \binom{XY}{L}\binom{XY - L}{D} \]

假设包含所有颜色的极小矩形为 \(H \times W\) ,只能保证 \(H \leq X, W \leq Y\) 。

回到原来的问题,选择一个 \(X \times Y\) 的矩形,对其进行染色。这个方案数同时也是:

“这个矩形是包含所有颜色的极小矩形” \(\cup\) “这个矩形任意缩近一条边后是包含所有颜色的矩形” 的方案数。

于是,将这些方案:

减去 \(\binom{4}{1}\) 种 “这个矩形任意缩近一条边后是包含所有颜色的矩形” 的方案后,不仅余下了 “这个矩形是包含所有颜色的极小矩形” 的方案,还多减了 \(\binom{4}{2}\) 种 “这个矩形任意缩近两条边后是包含所有颜色的矩形” 的方案。

加上 \(\binom{4}{2}\) 种 “这个矩形任意缩近两条边后是包含所有颜色的矩形” 的方案后,不仅余下了 “这个矩形是包含所有颜色的极小矩形” 的方案,还多加了 \(\binom{4}{3}\) 种 “这个矩形任意缩近三条边后是包含所有颜色的矩形” 的方案。

依次类推进行容斥即可得到答案。

形式化地:

\[\left \{ \begin{aligned} \mathbb{M} &= \mathbb{S} \cup \bigcup_{1 \leq i \leq 4} \mathbb{X}_i = \mathbb{S} + \bigcup_{1 \leq i \leq 4} \mathbb{X}_i \\ \mathbb{S} &= \mathbb{M} - \bigcup_{1 \leq i \leq 4} \mathbb{X}_i \\ &= \mathbb{M} - \sum_{1 \leq i \leq 4} \mathbb{X}_i + \bigcup_{1 \leq i < j \leq 4} \mathbb{X}_i \cap \mathbb{X}_j \\ &= \mathbb{M} - \sum_{1 \leq i \leq 4} \mathbb{X}_i + \sum_{1 \leq i < j \leq 4} \mathbb{X}_i \cap \mathbb{X}_j - \bigcup_{1 \leq i < j < k \leq 4} \mathbb{X}_i \cap \mathbb{X}_j \cap \mathbb{X}_k \\ &= \mathbb{M} - \sum_{1 \leq i \leq 4} \mathbb{X}_i + \sum_{1 \leq i < j \leq 4} \mathbb{X}_i \cap \mathbb{X}_j - \sum_{1 \leq i < j < k \leq 4} \mathbb{X}_i \cap \mathbb{X}_j \cap \mathbb{X}_k + \bigcup_{1 \leq i < j < k < l \leq 4} \mathbb{X}_i \cap \mathbb{X}_j \cap \mathbb{X}_k \cap \mathbb{X}_l \\ &= \mathbb{M} - \sum_{1 \leq i \leq 4} \mathbb{X}_i + \sum_{1 \leq i < j \leq 4} \mathbb{X}_i \cap \mathbb{X}_j - \sum_{1 \leq i < j < k \leq 4} \mathbb{X}_i \cap \mathbb{X}_j \cap \mathbb{X}_k + \sum_{1 \leq i < j < k < l \leq 4} \mathbb{X}_i \cap \mathbb{X}_j \cap \mathbb{X}_k \cap \mathbb{X}_l \\ \end{aligned} \right . \]

更具体地:

我们考虑对 \(X \times Y\) 的矩形染色,如图:

实际上得到的结果是“极小矩形的答案 + 四条边各忘内缩一格的答案”。

如图:

让 \(XY\) 的矩形,减去所有缩一条边的矩形,加上所有缩两条边的矩形,减去所有缩三条边的矩形,加上所有缩四条边的矩形。

一开始的矩形可以看作缩零条边。于是各自有 \(\binom{4}{0}, \binom{4}{1}, \binom{4}{2}, \binom{4}{3}, \binom{4}{4}\) 个。

定义

\[get(n, m) = \binom{n m}{L} \binom{n m - L}{D} \]

有:

加上:一个 get(X, Y)

减去:两个 get(X - 1, Y),两个 get(X, Y - 1)

加上:四个 get(X - 1, Y - 1),一个 get(X - 2, Y),一个 get(X, Y - 2)

减去:两个 get(X - 2, Y - 1),两个 get(X - 1, Y - 2)

加上:一个 get(X - 2, Y - 2)

view
const int MOD = 1E9 + 7;
const int MAXN = 1E6+10;
int add(int a, int b) { return ((1LL * a + b) % MOD + MOD) % MOD; }
int fac[MAXN], ifac[MAXN], inv[MAXN];
void solve() {
	fac[0] = fac[1] = ifac[0] = ifac[1] = inv[1] = 1;
	for (int i = 2; i < MAXN; i++) {
		inv[i] = 1LL * (MOD - MOD /  i) * inv[MOD % i] % MOD;
		fac[i] = 1LL * fac[i - 1] * i % MOD;
		ifac[i] = 1LL * ifac[i - 1] * inv[i] % MOD;
		assert(1LL * fac[i] * ifac[i] % MOD == 1);
	}
	auto comb = [&] (int n, int m) -> int {
		if (n < 0 || m < 0 || n < m) return 0;
		return 1LL * fac[n] * ifac[n - m] % MOD * ifac[m] % MOD;
	};
	int R, C; std::cin >> R >> C;
	int X, Y; std::cin >> X >> Y;
	int D, L; std::cin >> D >> L;

	int ans = 0;

	auto get = [&](int x, int y) -> int {
		if (x < 0 || y < 0) return 0;
		return 1LL * comb(x * y, D) % MOD * comb(x * y - D, L) % MOD;
	};

	// comb(4, 0) = 1
	ans = add(ans, get(X, Y));
	
	// comb(4, 1) = 4
	ans = add(ans, -get(X - 1, Y));
	ans = add(ans, -get(X - 1, Y));
	ans = add(ans, -get(X, Y - 1));
	ans = add(ans, -get(X, Y - 1));
	
	// comb(4, 2) = 6
	ans = add(ans, get(X - 1, Y - 1));
	ans = add(ans, get(X - 1, Y - 1));
	ans = add(ans, get(X - 1, Y - 1));
	ans = add(ans, get(X - 1, Y - 1));
	ans = add(ans, get(X - 2, Y));
	ans = add(ans, get(X, Y - 2));

	// comb(4, 3) = 4
	ans = add(ans, -get(X - 2, Y - 1));
	ans = add(ans, -get(X - 2, Y - 1));
	ans = add(ans, -get(X - 1, Y - 2));
	ans = add(ans, -get(X - 1, Y - 2));

	// comb(4, 4) = 1
	ans = add(ans, get(X - 2, Y - 2));

	
	ans = 1LL * ans * (R - X + 1) % MOD * (C - Y + 1) % MOD;
	std::cout << ans << "\n";
}

计算的时间复杂度 \(O(1)\) 。

但总时间复杂度依赖于组合数的值域 \(M \times T(1) = O(M)\) 。

posted @ 2024-07-15 03:59  03Goose  阅读(66)  评论(0)    收藏  举报