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\) 。
- 若 \(a, b\) 长度不相等,则一定无法相等。
- 否则 \(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\) 。
则有
不难注意到我们要从 \(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)\) 种可能的位置。
于是染色方案数为:
假设包含所有颜色的极小矩形为 \(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}\) 种 “这个矩形任意缩近三条边后是包含所有颜色的矩形” 的方案。
依次类推进行容斥即可得到答案。
形式化地:
更具体地:
我们考虑对 \(X \times Y\) 的矩形染色,如图:

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

让 \(XY\) 的矩形,减去所有缩一条边的矩形,加上所有缩两条边的矩形,减去所有缩三条边的矩形,加上所有缩四条边的矩形。
一开始的矩形可以看作缩零条边。于是各自有 \(\binom{4}{0}, \binom{4}{1}, \binom{4}{2}, \binom{4}{3}, \binom{4}{4}\) 个。

定义
有:
加上:一个 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)\) 。
浙公网安备 33010602011771号