数学不行,摆烂。
A. Shampoo
三个人 F, M, T 按顺序洗澡,其中洗发液用量分别是 $A, B, C$。
现在洗发液的量 $V$,问谁先无法洗澡?
分析
第一次没认真读,WA 了一发。
int main() {
int V, A, B, C;
cin >> V >> A >> B >> C;
V %= A + B + C;
if (V < A) {
cout << "F";
} else if (V < A + B) {
cout << "M";
} else {
cout << "T";
}
return 0;
}
全部代码请看:Atcoder/ABC243/ABC243A.cpp。
B. Hit and Blow
给定两个长为 $N$ 的无重复的整数序列 $\{A_i\},\{B_i\}$。找到满足以下条件的数量:
- 对于 $i$,使得 $A_i = B_i$。问 $i$ 有多少个。
- 对于 $(i,j)$,使得 $A_i = B_j$ 且 $i \neq j$。问 $(i,j)$ 有多少个。
分析
因为 $N$ 只有 $10^3$,可以非常暴力的计算。
int main() {
int n;
cin >> n;
vector<int> a(n), b(n);
for (auto &ai : a)
cin >> ai;
for (auto &bi : b)
cin >> bi;
int ans1 = 0;
for (int i = 0; i < n; i++)
ans1 += a[i] == b[i];
int ans2 = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j == i)
continue;
ans2 += a[i] == b[j];
}
}
cout << ans1 << "\n" << ans2;
return 0;
}
全部代码请看:Atcoder/ABC243/ABC243B.cpp。
C. Collision 2
给定 $N$ 个人的座标 $(x_i, y_i)$,并且每个人有两个行走的方向 $L,R$。问是否存在一个时间,使得两个人互相碰面?
分析
考虑所有 $y$ 座标相同的人,只需考虑最左侧的 $R$ 和最右侧的 $L$ 是否有碰面机会。
using int3 = tuple<int, int, int>;
int main() {
int n;
cin >> n;
vector<int> x(n), y(n);
for (int i = 0; i < n; i++) {
cin >> x[i] >> y[i];
}
string s;
cin >> s;
vector<int3> v(n + 1);
for (int i = 0; i < n; i++) {
v[i] = {x[i], y[i], s[i]};
}
v[n] = {-1, -1, -1};
sort(v.begin(), v.end(), [](int3 a, int3 b) {
return std::get<1>(a) < std::get<1>(b);
});
int l = -2E9, r = 2E9;
for (int i = 0; i < n - 1; i++) {
auto [x1, y1, s1] = v[i];
auto [x2, y2, s2] = v[i + 1];
if (y1 != y2) {
l = -2E9, r = 2E9;
}
if (s2 == 'L') {
l = max(l, x2);
} else {
r = min(r, x2);
}
if (l > r) {
cout << "Yes";
exit(0);
}
}
cout << "No";
return 0;
}
全部代码请看:Atcoder/ABC243/ABC243C.cpp。
D. Moves on Binary Tree
你现在有一个数字 $X$,做 $N$ 次操作:
- 如果是 $U$,则变成 $X \gets X / 2$。
- 如果是 $L$,则变成 $X \gets 2X$。
- 如果是 $R$,则变成 $X \gets 2X + 1$。
保证 $X$ 和答案不超过 $10^{18}$。
分析
注意到 $LU$ 和 $RU$ 可以消去,用栈不断的弹即可。
int main() {
ll N, X;
string S;
cin >> N >> X >> S;
string v;
for (auto si : S) {
if (si == 'U' && !v.empty() && v.back() != 'U') {
v.pop_back();
} else {
v.push_back(si);
}
}
for (auto vi : v) {
if (vi == 'L')
X = X * 2;
else if (vi == 'R')
X = X * 2 + 1;
else
X /= 2;
}
cout << X;
return 0;
}
全部代码请看:Atcoder/ABC243/ABC243D.cpp。
E. Edge Deletion
给定一个 $N$ 个点、$M$ 条边的图 $G$。你对这个图任意的删边得到 $G’$,但是要满足以下条件:
- 这张图仍是连通的。
- 任意两点 $s,t$ 在 $G’$ 中的最短距离仍是 $G$ 中的最短距离。
分析
贪心很好想到,证明不会。对于一条边,如果存在别的路径比这条边短(或一样长),那么这条边就可以去掉。
显然经过变换后图仍是连续的,因为最短路仍存在且未改变。
const int N = 300 + 10;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll G[N][N], f[N][N];
void floyd(int n);
int main() {
int n, m;
cin >> n >> m;
memset(G, 0x3f, sizeof(G));
memset(f, 0x3f, sizeof(f));
for (int i = 0; i < m; i++) {
ll ai, bi, ci;
cin >> ai >> bi >> ci;
G[ai][bi] = G[bi][ai] = ci;
f[ai][bi] = f[bi][ai] = ci;
}
for (int i = 1; i <= n; i++) {
f[i][i] = G[i][i] = 0;
}
floyd(n);
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (G[i][j] == INF)
continue;
bool flag = false;
for (int k = 1; k <= n; k++) {
if (i == k || j == k)
continue;
if (f[i][k] + f[k][j] <= G[i][j])
flag = true;
}
ans += flag;
}
}
cout << ans;
return 0;
}
全部代码请看:Atcoder/ABC243/ABC243E.cpp。
F. Lottery
一幅画可以获得 $N$ 种奖中的一种,每种中奖的概率是 $p_i = \frac{W_i}{\sum W_i}$。
问 $K$ 幅画获得恰好 $M$ 种不同的奖的概率是多少。
分析
我往容斥想了好久,没想明白。正解是用 DP 计算组合数。
若我们假定第 $i$ 种奖获得了 $c_i$ 次,那么其机率是
$$ \frac{K!}{\prod c_i!} \prod p_i^{c_i} = K! \prod_{i=1}^n\frac{p_i^{c_i}}{c_i!} $$
考虑 $DP[n][m][k]$,表示有 $n$ 种奖、获得了 $k$ 次奖、有 $m$ 种不同的奖。令 $S$ 是所有奖的全集,那么我们计算的是
$$ DP[n][m][k] = \sum_{C \subset S, |C| = m} K! \prod_{i=1}^n\frac{p_i^{c_i}}{c_i!} $$
可以观察多了第 $n$ 种奖时 DP 的转移情况,有刷表的转移方程(我将不清楚)。
const int N = 60;
int dp[N][N][N];
int main() {
pre_all(N * 10);
int n, m, k;
cin >> n >> m >> k;
vector<int> p(n);
int tsum = 0;
for (int i = 0; i < n; i++) {
cin >> p[i];
tsum = (tsum + p[i]) % P;
}
int iv_sum = qpow(tsum);
vector<int> pw[N];
for (int i = 0; i < n; i++) {
p[i] = 1ll * p[i] * iv_sum % P;
pw[i].resize(N);
pw[i][0] = 1;
int pi = p[i];
for (int j = 1; j < N; j++) {
pw[i][j] = 1ll * pi * ifac[j] % P;
pi = 1ll * pi * p[i] % P;
}
}
dp[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < n; j++) {
for (int x = 0; x <= k; x++) {
dp[i][j][x] = mo(dp[i][j][x] + dp[i - 1][j][x]);
for (int y = 1; y <= k - x; y++) {
ll mul = 1ll * dp[i - 1][j][x] * pw[i - 1][y];
dp[i][j + 1][x + y] = (dp[i][j + 1][x + y] + mul) % P;
}
}
}
}
cout << 1ll * dp[n][m][k] * fac[k] % P;
return 0;
}
全部代码请看:Atcoder/ABC243/ABC243F.cpp。
G. Sqrt
初始给定仅有一个数字的序列 $A = (X)$。设 $Y$ 是这个序列的最后一个数,你可以在后面压入 $1$ 到 $\lfloor\sqrt{Y}\rfloor$ 中的任何一个整数,如此操作 $10^{100}$ 次。
问最终序列 $A$ 有多少可能性。
分析
令 $f_n$ 是其解,容易得到
$$ f_n = \sum_{i=1}^{\lfloor\sqrt{n}\rfloor} f_i = f_{\lfloor n\rfloor^2} $$
因此答案只和 $\lfloor \sqrt{n} \rfloor$ 有关。现在的范围是 $3 \times 10^9$,仍需要优化。
令 $g_n = f_{n^2}$,可以得到
$$ g_n = g_{n-1} + g_{\lfloor \sqrt{n} \rfloor} = g_{\lfloor \sqrt{n} \rfloor^2} + (n - \lfloor \sqrt{n} \rfloor^2) g_{\lfloor \sqrt{n} \rfloor} $$
因此
$$ g_{(n+1)^2} = g_{(n+1)^2 - 1} + g_{n+1} = g_{n^2} + 2 n g_n + g_{n+1} $$
故可以预处理平方,复杂度再降到 4 次根号。还需注意 sqrt 精度问题,最好手写二分开根。
ll lsqrt(ll x); // 二分开根
const ll N = 1E5;
vector<ll> v1, v2;
void pre(ll n) {
v1.resize(n + 1), v2.resize(n + 1);
v2[1] = v1[1] = 1;
ll u = 2;
for (ll i = 2; i <= n; i++) {
if (u * u == i)
u++;
v1[i] = v1[i - 1] + v1[u - 1];
v2[i] = v2[i - 1] + 2 * (i - 1) * v1[i - 1] + v1[i];
}
}
int main() {
int T;
cin >> T;
pre(N);
while (T--) {
ll x;
cin >> x;
ll n = lsqrt(x), sn = lsqrt(n), ret = v2[sn];
cout << ret + (n - sn * sn) * v1[sn] << "\n";
}
return 0;
}
全部代码请看:Atcoder/ABC243/ABC243G.cpp。