Bondo416さんのNECプログラミングコンテスト2022 (AtCoder Beginner Contest 267)での成績:1576位
— ボンド@競プロ (@bond_cmprog) September 3, 2022
パフォーマンス:1268相当
レーティング:1550→1525 (-25) :(#AtCoder #NECプログラミングコンテスト2022(ABC267) https://t.co/t7lUmvvpkk
A - Saturday
if文で場合分け
void solve() { string S; cin >> S; int ans = 0; if (S == "Monday") ans = 5; else if (S == "Tuesday") ans = 4; else if (S == "Wednesday") ans = 3; else if (S == "Thursday") ans = 2; else ans = 1; cout << ans << endl; }
B - Split?
2つの列の選び方を全通り試し、条件を満たすものがあるかどうかを判定
void solve() { string S; cin >> S; string ans = "No"; vector<vector<int>> row(7); row[0] = vector({7}); row[1] = vector({4}); row[2] = vector({2, 8}); row[3] = vector({1, 5}); row[4] = vector({3, 9}); row[5] = vector({6}); row[6] = vector({10}); if (S[0] == '0') { REP(i, 7) REP(j, i + 2, 7) { bool ok1 = false; bool ok2 = false; for (auto x : row[i]) if (S[x - 1] == '1') ok1 = true; for (auto x : row[j]) if (S[x - 1] == '1') ok2 = true; if (!(ok1 and ok2)) continue; bool ok = true; for (int k = i + 1; k < j; k++) { for (auto x : row[k]) if (S[x - 1] == '1') ok = false; } if (ok) { cout << "Yes" << endl; return; } } } cout << "No" << endl; }
C - Index × A(Continuous ver.)
$ A_0 + 2A_1 + \dots + MA_M $ を一つ右にずらすと
$ A_1 + 2A_2 + \dots + (M - 1)A_M + MA_{M+1}$ となり、$ A_0 + A_1 + \dots + A_M $ を引いたあとに $ MA_{M+1} $ を加算することになる
そのため、もとの配列 $ A $ の累積和などを持ちながら値を更新していけばいい
void solve() { ll N, M; cin >> N >> M; vector<ll> A(N); REP(i, N) cin >> A[i]; ll sum = 0; ll sum_M = 0; REP(i, M) { sum += (i + 1) * A[i]; sum_M += A[i]; } ll ans = sum; REP(i, M, N) { sum -= sum_M; sum_M -= A[i - M]; sum_M += A[i]; sum += M * A[i]; chmax(ans, sum); } cout << ans << endl; }
D - Index × A(Not Continuous ver.)
$ dp[i][j] : = i $ 番目まで見ていて、$ j $ 個選んだときの最大値、としてDPを行う
void solve() { int N, M; cin >> N >> M; vector<ll> A(N); REP(i, N) cin >> A[i]; vector dp(M + 1, -LINF); dp[0] = 0; REP(i, N) { vector nxt(M + 1, -LINF); REP(j, M + 1) if (dp[j] > -LINF) { chmax(nxt[j], dp[j]); if (j + 1 <= M) chmax(nxt[j + 1], dp[j] + (j + 1) * A[i]); } swap(dp, nxt); } cout << dp[M] << endl; }
E - Erasing Vertices 2
操作のコストが$ X $ 以下の操作のみを行ってすべての頂点を消すことができるか、という判定問題を二分探索で求める
$ X $ 以下の操作が可能な頂点をstackなどで管理をし、操作が行える頂点から操作を行っていく
操作を行った際に、$ X $ 以下の操作が可能になった頂点を追加し、操作可能な頂点が空になるまで操作を行う
操作が終わった際に、すべての頂点を消すことができたかを判定する
void solve() { int N, M; cin >> N >> M; vector<ll> A(N); REP(i, N) cin >> A[i]; vector<vector<pair<int, int>>> g(N); vector<int> u(M), v(M); REP(i, M) { cin >> u[i] >> v[i]; --u[i], --v[i]; g[u[i]].emplace_back(v[i], i); g[v[i]].emplace_back(u[i], i); } auto check = [&](ll m) -> bool { vector<ll> sum(N); REP(i, M) { sum[u[i]] += A[v[i]]; sum[v[i]] += A[u[i]]; } vector<int> vertices; REP(i, N) if (sum[i] <= m) vertices.emplace_back(i); vector<int> used(N); while (!vertices.empty()) { int cur = vertices.back(); vertices.pop_back(); for (auto [x, i] : g[cur]) { if (used[x]) continue; sum[x] -= A[cur]; sum[cur] -= A[x]; if (sum[x] + A[cur] > m and sum[x] <= m) vertices.emplace_back(x); } used[cur] = 1; } if (accumulate(ALL(used), 0) == N) return true; else return false; }; ll l = -1, r = LINF; while (r - l > 1) { ll m = (r + l) >> 1; if (check(m)) r = m; else l = m; } cout << r << endl; }
F - Exactly K Steps
木の直径を求め、その頂点を $ (l, r) $ とする
$ (l, r ) $ を根としたときのパスをそれぞれdfsで求め、クエリの頂点 $ u_i $ に到達したときに距離が $ k_i $ 離れている頂点が求めたいものであるので、$ k_i $ 個前に訪れた頂点を求めればいい
void solve() { int N; cin >> N; treeDiameter tree(N); REP(i, N - 1) { int u, v; cin >> u >> v; tree.add_edge(--u, --v); } tree.build(0); int l = tree.v; int r = tree.w; auto &g = tree.G; int Q; cin >> Q; vector<vector<pair<int, int>>> q(N); REP(i, Q) { int u, k; cin >> u >> k; --u; q[u].emplace_back(i, k); } vector<int> ans(Q, -1); for (auto root : {l, r}) { vector<int> vertices; auto dfs = [&](auto &&self, int v, int par) -> void { for (auto [i, k] : q[v]) { if (k <= (int)vertices.size()) { ans[i] = vertices[(int)vertices.size() - k] + 1; } } vertices.emplace_back(v); for (auto nv : g[v]) { if (par == nv) continue; self(self, nv, v); } vertices.pop_back(); }; dfs(dfs, root, -1); } REP(i, Q) cout << ans[i] << endl; }