えんじにあ備忘録

脱よわよわエンジニアを目指すブログ

AtCoder Beginner Contest 290 に参加しました&反省会

AtCoder Beginner Contest 290 の振り返り

  • A、B問題を解くことができた
  • A問題: A を配列で受け取り、 B の数字を添え字として A_B  で総和を計算する(0相対に直す)- B問題:受け取った文字列を先頭から見ていく
  • C問題:手が出ず
  • D問題:愚直に問題文に沿って実装すると5ケースがTLEとなった

A問題

atcoder.jp

方針

  • 変数 ans を準備し、 0 で初期化する
  •  i に対して、 ans A[B_i]を加算する

B問題

atcoder.jp

方針

  • 文字列を前から見て先頭から K+1 個以降のoxに書き換える

解説*1から

実装例

  for(auto &nx : s){
    if(nx=='o'){
      if(k<=0){nx='x';}
      k--;
    }
  }
  • &nxとすることで書き換え
  • 書き換えるたびに kの値を減算する

C問題

atcoder.jp

解説*2から

  •  MEM  x+1 以上にするには x  B の要素として選ぶ必要がある。逆に、要素が重複しても MEM には影響しないので、重複を考慮しない
  •  i = 0,1,...,K - 1 まで、以下を繰り返す。
    • もし A  i が含まれる場合、それを選び出す。これにより、答えが  i+1 以上となることが確定する。
    • そうでない場合、 MEM の値を i より大きくすることはできないため、答えが i であると判明して終了する。
  • 上記が終了しても答えが判明しなかった場合、 B  (0,1,...,K - 1) の並べ替えとすることができているので、答えは K である。

実装例

int main(){
  int n,k;
  cin >> n >> k;
  set<int> st;
  for(int i=0;i<n;i++){
    int a;
    cin >> a;
    st.insert(a);
  }
  for(int i=0;i<k;i++){
    if(st.find(i)==st.end()){
      cout << i << "\n";
      return 0;
    }
  }
  cout << k << "\n";
  return 0;
}

D問題

atcoder.jp

方針

  • 問題文を愚直に実装する
int main () {
  
  int t;
  cin >> t;
  for (int i = 0; i < t; i++) {
    int n, d, k;
    cin >> n >> d >> k;
    vector<bool> mark(n, false);
    mark[0] = true;
    int count = 1;
    int last_mark = 0;
    for (int j = 0; j < n; j++) {
      if (count == k) {
        cout << last_mark << endl;
        break;
      }
      int x = (last_mark + d) % n;
      while (mark[x]) {
        x = (x + 1) % n;
      }
      mark[x] = true;
      last_mark = x;
      count++;
    }
  }

  return 0;
}

解説*3から

  • この問題を解くには以下の事実を知っておく必要がある。

     A,B を正整数とし、 A  Bの最大公約数を g とおく。 A=ag, B=bg と表されるとき、 a の個数 0,B\,mod\,A, 2B\,mod\,A,...,(a-1)B\,mod\,A の中には、 0,g,2g,...,(a-1)g (すなわち 0 以上 A 未満の g の倍数)がちょうど 1回ずつ現れる。

  • 最大公約数が 1 のときマスは一度もかぶらずに印をつけられる。それ以外のとき既に印がついているマスになり、 +1 したマスに印をつける。
  • 最大公約数 gcd  2 以上のとき次の通りになる
    •  i  (1 \leq i \leq n) 番目に印をつけるマスは ((i-1)D\,mod\,N) であり、これによって g の倍数のマス全てに印がつく。
    •  n+i  (1 \leq i \leq n) 番目に印をつけるマスは 1+((i-1)D\,mod\,N) であり、これによって g で割ると 1 余るマス全てに印がつく。
    •  2n+i  (1 \leq i \leq n) 番目に印をつけるマスは 2+((i-1)D\,mod\,N) であり、これによって g で割ると 2 余るマス全てに印がつく。
    • (以下同様) というように操作が進む。よって K 番目に印をつけるマスは [ \frac{K-1}{n} ]  +((K-1)D\,mod\,N)と計算できる。

実装例

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, d, k;
        cin >> n >> d >> k;
        --k;
        int a = n / gcd(n, d);
        cout << (long long) d * k % n + k / a << '\n';
    }
}