AtCoder abc321 参加メモ

SuntoryProgrammingContest2023(AtCoder Beginner Contest 321) - AtCoder

B - Cutoff

N ラウンド目に取り得る値を全探索する

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,x; cin >> n >> x;
  vector<int> a(n-1);
  REP(i,n-1) cin >> a[i];

  int ans = -1;
  REP(i,101) {
    auto b = a;
    b.push_back(i);
    sort(b.begin(),b.end());
    int tot = accumulate(b.begin()+1,b.end()-1,0);
    if (tot >= x) { ans = i; break; }
  }

  cout << ans << endl;
  return 0;
}

C - 321-like Searcher

x は最大で 9,876,543,210 になる。 同じ数は現れないため、0〜10 の数それぞれ1度だけ使うか、一度も使わないかどちらか。これは 210 と小さいため DFS で全列挙できる。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

int main() {
  int k; cin >> k; k--;

  vector<ll> ans;
  auto dfs = [&](auto self, ll i) -> void {
    ans.push_back(i);
    REP(ni,i%10) self(self,i*10+ni);
  };

  for(ll i = 1; i <= 9; i++) dfs(dfs,i);
  sort(ans.begin(),ans.end());
  cout << ans[k] << endl;
  return 0;
}

D - Set Menu

まず a, b は sort して問題ない。 a を固定して考えたとき、a[i] + b[j] >= p となる j の位置は二分探索で求まる。 このときの

  • p 未満となる合計は a[i] * j + (b[0] + b[1] + ... b[j-1])
  • p 以上となる合計は p * (m-j)

b[0] + b[1] + ... + b[x] は累積和を求めておくと O(1) で求められる

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

int main() {
  ll n,m,p; cin >> n >> m >> p;
  vector<ll> a(n),b(m);
  REP(i,n) cin >> a[i];
  REP(i,m) cin >> b[i];

  sort(a.begin(),a.end());
  sort(b.begin(),b.end());
  vector<ll> bs(m+1); // b の累積和
  REP(i,m) bs[i+1] = bs[i] + b[i];

  ll ans = 0;
  // a を固定して考える
  REP(i,n) {
    // a+b >= p となる b の index
    int idx = lower_bound(b.begin(),b.end(),p-a[i]) - b.begin();
    // a+b < p となる組み合わせの合計
    ans += a[i] * idx + bs[idx];
    // a+b >= p となる組み合わせの合計
    ans += (m-idx) * p;
  }
  cout << ans << endl;
  return 0;
}

雑感

MacOS アップデートしたら C++ コンパイル通らなくなるという... 開始5分前に気づいて焦った。結局ローカルでコンパイルできないままコンテスト始まってしまったため、AtCoder のコードテストで動作確認することにしてしのいだ。https://atcoder.jp/contests/apg4b/custom_test

E - Complete Binary Tree は時間が十分にあったものの、解ききれず。典型ぽくない adhoc な問題がでるとなかなか解けないな。

tic40さんのサントリープログラミングコンテスト2023(AtCoder Beginner Contest 321)での成績:1624位
パフォーマンス:1326相当
レーティング:1069→1097 (+28) :)
#AtCoder #サントリープログラミングコンテスト2023(ABC321) https://atcoder.jp/users/tic40/history/share/abc321?lang=ja 

AtCoder ARC 165 B - Sliding Window Sort 2

B - Sliding Window Sort 2

公式解説 を読んで AC したので紹介。

添字をバグらせ続けてなかなか苦戦したので、誰かの参考になれば。

Submission #45783935 - AtCoder Regular Contest 165

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,k; cin >> n >> k;
  vector<int> p(n);
  REP(i,n) cin >> p[i];

  vector<int> s(n); // s[i] := p[i-1] < p[i] が成り立つ個数の累積和
  REP(i,n-1) s[i+1] = s[i] + (p[i+1] > p[i] ? 1 : 0);
  vector<int> sm(n,1e9); // sm[i] := p[n-k]...p[i] の累積 min
  for(int i = n-k; i < n; i++) sm[i] = min(sm[i-1], p[i]);

  // l-r 間をソートして答えを出力
  auto ans = [&](int l = 0, int r = 0) {
    sort(p.begin()+l,p.begin()+r);
    for(auto v: p) cout << v << " ";
    exit(0);
  };

  REP(l,n) {
    int r = l+k-1;
    if (r >= n) break;
    // p[l] < p[l+1] < p[l+2] < ... < p[r] が成り立つ
    if (s[r] - s[l] == k-1) ans();
  }

  REP(l,n) {
    int r = l+k-1;
    if (r >= n) break;
    if (r < n-k) continue;
    // 条件1: p[l] < p[l+1] < ... < p[n-k] が成り立つ
    bool ok = (s[n-k] - s[l]) == n-k-l;
    // 条件2: p[n-k], p[n-k+1],..., p[r] の最小値は p[n-k-1] より大きい
    ok = ok && p[n-k-1] < sm[r];
    if (ok) ans(l,r+1);
  }

  ans(n-k,n);
  return 0;
}

AtCoder abc320 参加メモ

Toyota Programming Contest 2023#5(AtCoder Beginner Contest 320) - AtCoder

B - Longest Palindrome

二重ループで全探索。回文は reverse した文字列と等しいかどうかで判定

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  string s; cin >> s;
  int n = s.size();

  auto check = [&](string s) {
    auto t = s;
    reverse(t.begin(),t.end());
    return t == s;
  };

  int ans = 0;
  REP(i,n) for(int j = 1; j+i <= n; j++) {
    if (check(s.substr(i,j))) ans = max(ans,j);
  }
  cout << ans << endl;
  return 0;
}

C - Slot Strategy 2 (Easy)

  • リールが全て同じ数で止まるケースは 9 通りしかない(000,111,222,...,999)
  • 同じ時間に止められるリールは一つ。またリールはどれから止めても良いため、止める順番は 3! = 6 通りある

上記をうまく探索する。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
const int INF = numeric_limits<int>::max();

// リールを止める順番
const vector<vector<int>> orders = {
  { 0,1,2 },{ 0,2,1 },
  { 1,0,2 },{ 1,2,0 },
  { 2,0,1 },{ 2,1,0 }
};

int main() {
  int m; cin >> m;
  vector<string> s(3);
  REP(i,3) cin >> s[i];
  // g[i][j] := i 番目のリールでスロットの表示に j が現れる時間の集合  
  vector g(3,vector(10,vector<int>()));
  REP(i,3) REP(j,m) g[i][s[i][j]-'0'].push_back(j);

  auto impossible = [&](int i) {
    REP(j,3) if (g[j][i].size() == 0) return true;
    return false;
  };

  int ans = INF;
  REP(i,10) {
    if (impossible(i)) continue; // i|i|i でリールを止められるか
    for(auto order: orders) {
      int t = 0;
      for(int reel: order) {
        int modt = t%m;
        auto it = lower_bound(g[reel][i].begin(),g[reel][i].end(),modt);
        if (it == g[reel][i].end()) t += m - modt + g[reel][i][0];
        else t += *it - modt;
        t += 1;
      }
      ans = min(ans,t-1);
    }
  }

  cout << (ans == INF ? -1 : ans) << endl;
  return 0;
}

D - Relative Position

絶対座標がわかっているのは人 1 のみ。グラフとして考えて人 1 から到達可能な点は絶対座標がわかる。DFS で到達不可能な点は undecidable となる。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;
using P = pair<int,pair<ll,ll>>;

int main() {
  int n,m; cin >> n >> m;
  vector<int> a(m),b(m);
  vector<ll> x(m),y(m);
  vector g(n,vector<P>());

  REP(i,m) {
    cin >> a[i] >> b[i] >> x[i] >> y[i];
    a[i]--; b[i]--;
    g[a[i]].push_back({ b[i], {x[i],y[i]} });
    g[b[i]].push_back({ a[i], {-x[i],-y[i]} });
  }

  vector<pair<ll,ll>> ans(n);
  ans[0] = {0,0};
  vector<bool> visited(n);
  auto dfs = [&](auto self, int i) -> void {
    for(auto [v,pos]: g[i]) {
      if (visited[v]) continue;
      visited[v] = true;

      auto [nx,ny] = pos;
      auto [px,py] = ans[i];
      ans[v] = { px+nx, py+ny };
      self(self,v);
    }
  };
  visited[0] = true;
  dfs(dfs,0);

  REP(i,n) {
    if (!visited[i]) cout << "undecidable" << endl;
    else cout << ans[i].first << " " << ans[i].second << endl;
  }
  return 0;
}

E - Somen Nagashi

そうめん流しに並んでいる人、列から外れた人をそれぞれ2つの優先度付きキューに入れる。

  1. 出来事 i が起こったとき、列から外れた人のキューにその時間内に戻ってくる人がいたら、キューから出しそうめん流しに並んでいる人のキューへ加える。
  2. そうめん流しに並んでいる人のキューから番号の最も小さい人がそうめんを食べる。
  3. そうめんを食べた人を列から外れた人のキューへ追加する。
#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;
using P = pair<ll,int>;

int main() {
  int n,m; cin >> n >> m;
  vector<ll> t(m),w(m),s(m);
  REP(i,m) cin >> t[i] >> w[i] >> s[i];

  // そうめん流しに並んでいる人
  priority_queue<int, vector<int>, greater<int>> pq1;
  // 列から外れた人
  priority_queue<P, vector<P>, greater<P>> pq2;
  REP(i,n) pq1.push(i);

  vector<ll> ans(n);
  REP(i,m) {
    // 列へ戻ってくる人の処理
    while(pq2.size()) {
      auto [vt,vi] = pq2.top();
      if (vt > t[i]) break;
      pq2.pop(); pq1.push(vi);
    }
    // そうめんを食べる人の処理
    if (pq1.empty()) continue;
    auto vi = pq1.top(); pq1.pop();
    ans[vi] += w[i];
    pq2.emplace(t[i]+s[i],vi);
  }

  for(auto v: ans) cout << v << endl;
  return 0;
}

雑感

直近 5 回のコンテストで酷い結果を連発しレートは highest から 150 も下がる始末。今日こそはなんとかしたかった。前回といい近頃の C 問題は難しいね。 今回は E まで解けたので 1000番台 に入ってるかと思いきや 2000 番台。 F 問題は動的計画法で解く方針を立てたものの時間が足りず。解くスピードが足りないな。

AtCoder abc312 参加メモ

UNIQUE VISION Programming Contest 2023 Summer(AtCoder Beginner Contest 312) - AtCoder

B - TaK Code

制約が小さいので、すべての (i,j) から 9x9 マスを条件に沿っているか全探索すればいい。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,m; cin >> n >> m;
  vector<string> s(n);
  REP(i,n) cin >> s[i];

  // 座標(x,y) から 3x3マスの領域がすべて黒 かつ 8方位隣接マスが全て白か?
  auto f = [&](int x, int y, vector<vector<int>> v) -> bool {
    REP(i,5) REP(j,5) {
      int nx = x-1+i, ny = y-1+j;
      if (nx < 0 || ny < 0 || nx >= 9 || ny >= 9) continue;
      // 3x3の外
      if (nx < x || ny < y || nx > x+2 || ny > y+2) {
        if (v[nx][ny] == '#')  return false;
      } else {
        if (v[nx][ny] == '.')  return false;
      }
    }
    return true;
  };

  REP(i,n-8) REP(j,m-8) {
    vector v(9, vector<int>(9));
    REP(k,9) REP(l,9) v[k][l] = s[i+k][j+l];
    if (f(0,0,v) && f(6,6,v)) cout << i+1 << " " << j+1 << endl;
  }
  return 0;
}

C - Invisible Hand

単調性があるため二分探索できる。 金額が x のときの売り手、買い手の数を求め方に注意する。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,m; cin >> n >> m;
  vector<int> a(n),b(m);
  REP(i,n) cin >> a[i];
  REP(i,m) cin >> b[i];
  sort(a.begin(),a.end());
  sort(b.begin(),b.end());

  int ok = 1e9+5, ng = 0;
  while(abs(ok - ng) > 1) {
    int mid = (ok+ng)/2;
    int seller = upper_bound(a.begin(),a.end(),mid) - a.begin();
    int buyer = m - (lower_bound(b.begin(),b.end(),mid) - b.begin());
    if (seller >= buyer) ok = mid;
    else ng = mid;
  }
  cout << ok << endl;
  return 0;
}

D - Count Bracket Sequences

DP がうまく書けず、ACできず。悔しすぎる。

成績

緑落ち

tic40さんのユニークビジョンプログラミングコンテスト2023 夏 (AtCoder Beginner Contest 312)での成績:3502位
パフォーマンス:840相当
レーティング:1207→1175 (-32) :(
#AtCoder #ユニークビジョンプログラミングコンテスト2023夏(ABC312) https://atcoder.jp/users/tic40/history/share/abc312?lang=ja 

AtCoder abc311 参加メモ

Toyota Programming Contest 2023#4(AtCoder Beginner Contest 311) - AtCoder

B - Vacation Together

全探索。d 日目まで見ていき、全員が暇ならカウント+1, 一人でも暇でなければカウントリセットする。 カウントが最大の値が答え

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,d; cin >> n >> d;
  vector<string> s(n);
  REP(i,n) cin >> s[i];

  int ans = 0, now = 0;
  REP(i,d) {
    bool ok = true;
    REP(j,n) if (s[j][i] == 'x') ok = false;
    if (ok) now++;
    else now = 0;
    ans = max(ans,now);
  }
  cout << ans << endl;
  return 0;
}

C - Find it!

強連結成分分解で解いた。 dfs を 2 回行うことで閉路のグループに分解できる( 参考: https://manabitimes.jp/math/1250 )

ちなみに ACL にも SCC ライブラリがあるが、得られる頂点の順序がバラバラなため順序は正しく出力する必要がある。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n; cin >> n;
  vector<vector<int>> g(n),gr(n);
  REP(i,n) {
    int a; cin >> a; a--;
    g[i].push_back(a);
    gr[a].push_back(i);
  }

  vector<int> dir;
  vector<bool> used(n);
  auto dfs = [&](auto self, int i) -> void {
    if (used[i]) return;
    used[i] = true;
    for(auto v: g[i]) self(self,v);
    dir.push_back(i);
  };

  REP(i,n) dfs(dfs,i);
  auto dir1 = dir;
  reverse(dir1.begin(), dir1.end());

  swap(g,gr);
  used = vector<bool>(n);
  for(int i: dir1) {
    dir = vector<int>();
    dfs(dfs,i);
    if (dir.size() == 1) continue;
    cout << dir.size() << endl;
    for(auto v: dir) cout << v+1 << " ";
    break;
  }
  return 0;
}

D - Grid Ice Floor

dfs で到達可能なすべてのマス x 4 方向への移動 を調べる。通常の dfs に 4 方向の移動を拡張した形。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

const vector<pair<int,int>> moves = { {-1,0}, {0,1}, {1,0}, {0,-1} };

int main() {
  int n,m; cin >> n >> m;
  vector<string> s(n);
  REP(i,n) cin >> s[i];

  // [i][j][k] := i,j マスで k 方向 への移動を試したかどうか
  vector seen(n,vector(m,vector<bool>(4)));
  // [i][j] := i,j マスを通過したかどうか
  vector visited(n,vector<int>(m));

  auto dfs = [&](auto self, int x, int y) -> void {
    REP(k,4) {
      if (seen[x][y][k]) continue;
      seen[x][y][k] = true;
      auto [dx,dy] = moves[k];
      int nx = x, ny = y;
      // 岩にぶつかるまで進む
      while(s[nx+dx][ny+dy] == '.') {
        nx += dx; ny += dy;
        visited[nx][ny] = 1;
      }
      self(self,nx,ny);
    }
  };

  visited[1][1] = true;
  dfs(dfs,1,1);
  int ans = 0;
  REP(i,n) REP(j,m) ans += visited[i][j];
  cout << ans << endl;
  return 0;
}

E - Defect-free Squares

DP。 dp[i][j] := i,j マスまでで、i,j マスを含んで作れる穴のない正方形の総数。 i,j マスが穴でない場合、左、左上、上のマスで min を取ったもの + 1(i,jマスのみで正方形を作る分) となる。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

int main() {
  int h,w,n; cin >> h >> w >> n;
  vector g(h,vector<bool>(w));
  REP(i,n) {
    int a,b; cin >> a >> b;
    a--; b--;
    g[a][b] = true;
  }

  // dp[i][j] := i,j マスまでで、i,j を含んで作れる穴のない正方形の総数
  vector dp(h,vector<ll>(w));
  REP(i,h) REP(j,w) {
    if (g[i][j]) continue;
    dp[i][j] = 1;
    if (i-1 >= 0 && j-1 >= 0) dp[i][j] += min({ dp[i-1][j-1], dp[i-1][j], dp[i][j-1] });
  }

  ll ans = 0;
  REP(i,h) REP(j,w) ans += dp[i][j];
  cout << ans << endl;
  return 0;
}

今回の成績

tic40さんのトヨタ自動車プログラミングコンテスト2023#4(AtCoder Beginner Contest 311)での成績:1472位
パフォーマンス:1357相当
レーティング:1189→1207 (+18) :)
Highestを更新し、4 級になりました!
#AtCoder #トヨタ自動車プログラミングコンテスト2023#4(ABC311) https://atcoder.jp/users/tic40/history/share/abc311?lang=ja 

AtCoder abc310 参加メモ

freee Programming Contest 2023(AtCoder Beginner Contest 310) - AtCoder

B - Strictly Superior

全探索。p[i] == p[j] の場合など条件がややこしいため注意する

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,m; cin >> n >> m;
  vector<int> p(n),c(n);
  vector f(n,vector<bool>(m));
  REP(i,n) {
    cin >> p[i] >> c[i];
    REP(j,c[i]) {
      int x; cin >> x; x--;
      f[i][x] = true;
    }
  }

  REP(i,n) REP(j,n) {
    if (i == j) continue;
    if (p[i] > p[j]) continue;
    bool ok = true;
    int cnt = 0; // i のみにある機能の個数
    REP(k,m) {
      if (f[j][k] && !f[i][k]) ok = false;
      if (f[i][k] && !f[j][k]) cnt++;
    }
    if (p[i] == p[j]) ok = ok && cnt > 0;
    if (ok) { cout << "Yes" << endl; return 0; }
  }

  cout << "No" << endl;
  return 0;
}

C - Reversible

set を使って通常と逆順両方がまだ集合になかったら追加する

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n; cin >> n;
  unordered_set<string> st;

  REP(i,n) {
    string s; cin >> s;
    if (st.count(s)) continue;
    reverse(s.begin(),s.end());
    if (st.count(s)) continue;
    st.insert(s);
  }

  cout << st.size() << endl;
  return 0;
}

D - Peaceful Teams

DFS。

チームは区別されないので、まだ誰もいないチームに人を入れるときには、一つのチームだけで良い(この場合どの空のチームに入れても区別されないため)

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,t,m; cin >> n >> t >> m;
  vector c(n,vector<int>(n)); // c[i][j] := i,j の相性が悪いかどうか
  REP(i,m) {
    int a,b; cin >> a >> b;
    a--; b--;
    c[a][b] = c[b][a] = 1;
  }

  vector g(t,vector<int>()); // チームに所属しているメンバー情報
  auto dfs = [&](auto self, int i) -> int {
    if (i == n) {
      REP(j,t) if (g[j].size() == 0) return 0;
      return 1;
    }

    int res = 0;
    REP(j,t) {
      bool ok = true; // j チームに i を追加可能かどうか
      for(auto v: g[j]) if (c[i][v]) ok = false;
      if (!ok) continue;

      g[j].push_back(i);
      res += self(self,i+1);
      g[j].pop_back();
      if (g[j].size() == 0) break;
    }
    return res;
  };

  cout << dfs(dfs, 0) << endl;
  return 0;
}

今回の成績

tic40さんのfreee プログラミングコンテスト2023(AtCoder Beginner Contest 310)での成績:1772位
パフォーマンス:1315相当
レーティング:1174→1189 (+15) :)
Highestを更新しました!
#AtCoder #freeeプログラミングコンテスト2023(ABC310) https://atcoder.jp/users/tic40/history/share/abc310?lang=ja 

AtCoder abc309 参加メモ

Denso Create Programming Contest 2023 (AtCoder Beginner Contest 309) - AtCoder

B - Rotate

愚直に時計回りへずらしていった。もっと簡単な方法あるかな。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n; cin >> n;
  vector<string> a(n);
  REP(i,n) cin >> a[i];

  auto b = a;
  REP(j,n-1) b[0][j+1] = a[0][j];
  REP(i,n-1) b[i+1][n-1] = a[i][n-1];
  for(int j = n-1; j > 0; j--) b[n-1][j-1] = a[n-1][j];
  for(int i = n-1; i > 0; i--) b[i-1][0] = a[i][0];

  REP(i,n) {
    REP(j,n) cout << b[i][j];
    cout << endl;
  }
  return 0;
}

C - Medicine

累積和で何日目に何錠飲むかを求めて、k 以下になった日が答え。

$ a_i $ の制約が大きいため map で座標圧縮した。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
using ll = long long;

int main() {
  int n,k; cin >> n >> k;
  map<int,ll> mp;
  REP(i,n) {
    int a,b; cin >> a >> b;
    mp[1] += b; mp[1+a] -= b;
  }

  ll p = 0;
  for(auto [i,v]: mp) {
    mp[i] += p;
    if (mp[i] <= k) { cout << i << endl; return 0; }
    p = mp[i];
  }

  return 0;
}

D - Add One Edge

頂点1 と 頂点 N1+N2 からそれぞれ bfs で最短経路を求めて、それぞれの最大の距離の地点同士を連結させると d が最大値となる

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'
const int INF = numeric_limits<int>::max();

int main() {
  int n1,n2,m; cin >> n1 >> n2 >> m;
  int n = n1+n2;
  vector g(n,vector<int>());
  REP(i,m) {
    int a,b; cin >> a >> b;
    a--; b--;
    g[a].push_back(b);
    g[b].push_back(a);
  }

  auto bfs = [&](int i) {
    vector<int> dist(n,INF);
    queue<int> q;
    q.push(i);
    dist[i] = 0;
    while(q.size()) {
      int v = q.front(); q.pop();
      for(auto nv: g[v]) {
        if (dist[nv] != INF) continue;
        q.push(nv);
        dist[nv] = dist[v]+1;
      }
    }
    return dist;
  };

  auto d1 = bfs(0);
  auto d2 = bfs(n-1);
  int ans1 = *max_element(d1.begin(), d1.begin() + n1);
  int ans2 = *max_element(d2.begin() + n1, d2.end());
  int ans = ans1 + ans2 + 1;
  cout << ans << endl;
  return 0;
}
}

E - Family and Insurance

親から子へ dfs する。 引数に最大何世代先まで補償できるかを持っておき、今の人が保険に入っていたら親からの補償と自身の保険の補償との最大の世代数を次へ渡していけばいい。

#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<n;i++)
#define endl '\n'

int main() {
  int n,m; cin >> n >> m;
  vector g(n,vector<int>());
  for(int i = 1; i < n; i++) {
    int p; cin >> p; p--;
    g[p].push_back(i);
  }

  vector<int> d(n);
  REP(i,m) {
    int x,y; cin >> x >> y; x--;
    d[x] = max(d[x],y);
  }

  auto bfs = [&](auto self, int i, int p) -> int {
    int res = 0;
    if (p > 0 || d[i] > 0) res++;
    int np = max(p-1,d[i]);
    for(auto v: g[i]) res += self(self,v,np);
    return res;
  };

  cout << bfs(bfs,0,0) << endl;
  return 0;
}

今回の成績

tic40さんのデンソークリエイトプログラミングコンテスト2023(AtCoder Beginner Contest 309)での成績:1541位
パフォーマンス:1285相当
レーティング:1161→1174 (+13) :)
Highestを更新しました!
#AtCoder #デンソークリエイトプログラミングコンテスト2023(ABC309) https://atcoder.jp/users/tic40/history/share/abc309?lang=ja