ABC 405
Table of Contents
https://atcoder.jp/contests/abc405
D - Escape Route
まず複数ある非常口から通路マスへの距離の最短を求める。 マス $(i,j)$ から隣のマスへの距離が -1 のとき、その方向に進む記号を記録して出力する。
void solve() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
ll H, W;
cin >> H >> W;
vector<string> grid(H);
rep(i, H) cin >> grid[i];
int si = 0, sj = 0;
vector<pair<int, int>> exits;
rep(i, H) rep(j, W) {
if (grid[i][j] == 'E')
exits.push_back({i, j});
}
// i,j, cost
vvll dist(H, vll(W, INF));
deque<tuple<ll, ll, ll>> deq;
for (auto [i, j] : exits) {
deq.push_back({i, j, 0});
dist[i][j] = 0;
}
vll di = {0, 1, 0, -1};
vll dj = {1, 0, -1, 0};
while (deq.size()) {
auto [nowi, nowj, cost] = deq.front();
deq.pop_front();
if (dist[nowi][nowj] < cost)
continue;
rep(dir, 4) {
ll ni = nowi + di[dir], nj = nowj + dj[dir];
if (clamp(ni, 0ll, H - 1) != ni || clamp(nj, 0ll, W - 1) != nj)
continue;
if (grid[ni][nj] == '#')
continue;
if (dist[ni][nj] <= cost + 1)
continue;
dist[ni][nj] = cost + 1;
deq.push_back({ni, nj, dist[ni][nj]});
}
}
vector<char> dir_mark = {'>', 'v', '<', '^'};
vector<string> ans = grid;
rep(i, H) {
rep(j, W) {
if (grid[i][j] == '#' || grid[i][j] == 'E')
continue;
rep(dir, 4) {
ll ni = i + di[dir], nj = j + dj[dir];
if (clamp(ni, 0ll, H - 1) != ni || clamp(nj, 0ll, W - 1) != nj)
continue;
if (dist[i][j] == dist[ni][nj] + 1) {
ans[i][j] = dir_mark[dir];
}
}
}
}
for (auto s : ans)
cout << s << endl;
}
E - Fruit Lineup
解説 AC
無駄に難しく考えてしまって TLE コードを書いてしまった。
TLE 方針
$\mathrm{comb}(n,r) = \frac{n!}{(n-r)!r!}$ とする。
一番右端のリンゴの位置を $k$ とするとリンゴより左に来るのはオレンジだけだから $A \leq k \leq A+B$ の範囲で $k$ は動かせる。 $k$ を固定したとき、$k$ より左側には $A-1$ 個のリンゴと $k-A$ 個のオレンジがある。これら $k-1$ 個の並べ方は $\mathrm{comb}(k-1, A-1)$ 通りである。
$k$ より右側ではまずオレンジとブドウをこの順に並べた後($B-(k-A) + D$ 個並んでいる)、両端と間の $B-(k-A)+D+1$ 箇所から $m$ 箇所を選んでバナナを1個以上挿入すると考えた。 $m$ を固定したとき $C$ 個を $m$ 分割する方法は $\mathrm{comb}(C-1){m-1}$ 通りである。($C$ 個のバナナを並べたときの隙間 $C-1$ からどこで区切るかの $m-1$ 個通りの組み合わせ)
以上をまとめると右側の並べ方は
\begin{align*} \sum_{k=A}^{A+B} \left\{ \mathrm{comb}(k-1, A-1) \cdot \left( \sum_{m=1}^{C} \mathrm{comb}(B-(k-A)+D+1, m) \cdot \mathrm{comb}(C-1, m-1) \right) \right\} \end{align*}
これだと最大で $O(10^{12})$ くらいかかるので TLE する。
想定解法
オレンジ、バナナ、ブドウの並べ方のところをより効率化できる。 $B-(k-A)+C+D$ 箇所からバナナを配置する $C$ 箇所を選び、残りの場所にはオレンジ・ブドウの順に並べればよい。
これだとループが一つになるので $O(A+B)$ で済む。
\begin{align*} \sum_{k=A}^{A+B} \mathrm{comb}(k-1, A-1) \cdot \mathrm{comb}(B-(k-A)+C+D, C) \end{align*}
公式解説ではブドウ基準に解いている。
void solve() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
ll A, B, C, D;
cin >> A >> B >> C >> D;
ll tot = A + B + C + D;
vector<mint> fac(tot + 1), invfac(tot + 1);
fac[0] = invfac[0] = 1;
rep2(i, 1, tot + 1) {
fac[i] = fac[i - 1] * i;
}
rep2(i, 1, tot + 1) {
invfac[i] = fac[i].inv();
}
auto comb = [&](ll n, ll r) -> mint {
return fac[n] * invfac[n - r] * invfac[r];
};
mint ans = 0;
for (ll k = A; k <= A + B; k++) {
ll left_orange = k - A;
ll right_orange = B - left_orange;
ans += comb(k - 1, A - 1) * comb(right_orange + C + D, C);
}
// rep(k, C + 1) {
// ans += comb(A + B + k, B) * comb(D - 1 + C - k, C - k);
// }
cout << ans.val() << endl;
}
F - Chord Crossing
解説 AC
https://atcoder.jp/contests/abc405/editorial/13011 の方式で実装
void solve() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int N, M;
cin >> N >> M;
vector<pair<int, int>> es;
rep(i, M) {
int a, b;
cin >> a >> b;
a--, b--;
es.emplace_back(a, b);
}
sort(all(es), [](auto a, auto b) -> bool {
return a.second < b.second;
});
int Q;
cin >> Q;
// c, d, index
vector<tuple<int, int, int>> query;
rep(i, Q) {
int c, d;
cin >> c >> d;
query.emplace_back(c, d, i);
}
sort(all(query), [](auto a, auto b) -> bool {
return get<1>(a) < get<1>(b);
});
fenwick_tree<int> fw(N * 2);
rep(i, M) {
fw.add(es[i].first, 1);
}
vint ans(Q);
int k = 0;
for (auto [c, d, id] : query) {
while (k < M && es[k].second < d) {
fw.add(es[k].first, -2); // 1 -> -1 に変更
fw.add(es[k].second, 1);
k++;
}
ans[id] = fw.sum(c, d);
}
for (int x : ans)
cout << x << '\n';
}