秘密の本棚

気になることをなんでも書きます

AtCoder Beginner Contest 106

AtCoder Beginner Contest 106 - AtCoder

A - Garden

問題

A - Garden

考察

 A、横 Bの長方形から縦 A、横 1の長方形と縦 1、横 Bの長方形を切り取った残りの面積を求める。これらの長方形は平行移動しても残りの面積が不変であるから、端に寄せれば残りは縦 A-1、横 B-1の長方形となる。

コード

int main(){
  ll a,b;
  cin >> a >> b;
  cout << (a-1)*(b-1) << endl;
  return 0;
}

B - 105

問題

B - 105

考察

 1以上 N以下の奇数で約数を8個もつものの個数を答える。約数の個数をカウントする関数を書いて、ループで回せばよい。

コード

ll cnt_n(ll n){
  ll cnt = 0;
  for(int i = 1; i <= n; i++){
    if(n%i==0) cnt++;
  }
  return cnt;
}
 
int main(){
  ll n;
  cin >> n;
  ll cnt = 0;
  for(int i = 1; i <= n; i+=2){
    if(cnt_n(i)==8){
      cnt++;
    }
  }
  cout << cnt << endl;
  return 0;
}

C - To Infinity

考察

与えられた文字列に対し、1以外の文字は無限に付け足されると考えてよく、そのとき K文字目に何が現れるかを答える。したがって左から順に見ていって K文字目までに1しかなければ答えは1だが、それ以外の文字が現れたら即時にその文字が答えとなる。

コード

int main(){
  str s;
  cin >> s;
  ll k;
  cin >> k;
  REP(i,s.length()){
    if(s[i]!='1'&&k>i){
      cout << s[i] << endl;
      return 0;
    }
  }
  cout << 1 << endl;
  return 0;
}

D - AtCoder Express 2

考察

区間 \left[1,N\right]に含まれる M個の区間が与えられ、その後 Q回別途与えられる区間内に、 M個のうちいくつの区間が含まれるかをそれぞれ答える。 Q 10^5まで大きくなりうるので、各クエリに対しては O(N)以下で答える必要がある。
各クエリに対して O(1)で答えるには、区間 \left[p,q\right]に含まれる区間の個数を配列 A[p][q]としてもっておけばよい。この配列を効率よく計算する方法を考える。
 M個与えられる区間のうちの1つを \left[l_i,r_i\right]とする。ここで、この区間が答えとしてカウントされるようなクエリの条件は、クエリを \left[p,q\right]として p\leq l_i\text{かつ}q\geq r_iである。したがって、これを満たすようなすべての (p,q)に対して A[p][q]を1加えるという操作を行えば、配列 Aが完成する。しかし、これに必要なオーダーは O(N^2)となって、 M個の区間に対して繰り返すには大きすぎる。
そこで、区間 \left[l_i,r_i\right]に対して加算される Aの範囲は長方形であることに着目する。このような場合、累積和を用いるとオーダーを落とすことができる。 Aと同じサイズの配列 Bを用意し、先ほどの1加える範囲の最初に +1を、最後に -1を行ごとに置いておく。すると、 Bの累積和を取っていけば求めたかった Aが現れる。 Bの生成が O(MN)、累積和の計算が O(N^2)なので間に合う。

コード

int main(){ll n,m,q;
  cin >> n >> m >> q;
  ll array[n][n+1];
  ll sum[n][n+1];
  fill(array[0],array[n],0);
  REP(i,m){
    ll l,r;
    cin >> l >> r;
    l--, r--;
    for(ll k = 0; k <= l; k++){
      array[k][r]++;
      array[k][n]--;
    }
  }
  ll val = 0;
  REP(i,n){
    REP(j,n+1){
      val += array[i][j];
      sum[i][j] = val;
    }
  }
  REP(i,q){
    ll p,q;
    cin >> p >> q;
    p--, q--;
    cout << sum[p][q] << endl;
  }
  return 0;

雑感

今回は運良くかなり好調で、24分で全完できました。累積和が使える問題の頻度は結構高い気がします。
継続は力なりなので、毎週参加していきたいです。