chacoderのブログ

競技プログラミングそのほか

ABC188参戦記

f:id:chacoder:20210111113136p:plain

f:id:chacoder:20210111113244p:plain

はじめに

2021年1月10日 21:00-21:40 ABC188に参戦しました。

atcoder.jp

結果は92分51秒 4完1ペナ 2618位/7831人中でパフォーマンス949
レーティングは前回より+16の814となり5度目の入緑を果たしました。

A問題

バスケットボールで現在X:Yの得点のとき劣勢の方が3ポイントシュートを決めて逆転優勢になれるかという問題です。

丁寧に実装してサンプルを通し2分でAC。

B問題

2次元ベクトルの内積が0になるかどうかを判定する問題。
Nの制約が<=100000、個別の要素の積は100*100以下で10000までなのでINT型で間に合います。

3分24秒(通算5分24秒)で提出しAC。

ゆっくり目ですがつまらないミスでペナルティを出さないようにすることを優先しています。
後半の考察で相当時間をとられるので、今のところはA問題やB問題あたりで多少時間をかけることは気にしないようにしています。

C問題

C - ABC Tournament

トーナメント戦の準優勝者を求める問題。
2つの山のMAXをとり,少ない方(準優勝者)のIDを特定する2段階で解答を出します。

問題文を正確に理解するのにちょっと手間取りましたが、気づけば簡単です。

8分14秒(通算13分38秒)でAC。

エスティメーターみるとこのあたりで推定パフォ1100ぐらいが出ててちょっと嬉しかったですが、後半が解けないとずるずる下がってぬか喜びになりがちなので、気を引き締めて先に進みます。

提出コード

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
#define rep(i, n) for (int i = 0; i < n; i++)
#define all(v) v.begin(), v.end()

int main(){
  int N;
  cin>>N;
  int n=pow(2,N);
  int A[n];
  rep(i, n) cin>>A[i];
  int maxa=0;
  int maxb=0;
  rep(i, n/2){
    maxa=max(A[i],maxa);
  }
  for(int i=(n/2);i<n;i++){
    maxb=max(A[i],maxb);
  }
  int num=min(maxa,maxb);
 rep(i, n){
   if(A[i]==num){
     cout<<i+1<<endl;
     return 0;
   }
 }
  return 0;
}

D問題

D - Snuke Prime

よくありがちな問題ですが制約がとても大きいのでどんな工夫をすべきか悩みました。
途中、あきらめかけましたが、必ず解けると信じて最後まで粘れたのがよかったです。

考察

 日数が10^9と非常に長いので普通に配列に収納することすらできません。

 最初に考えたのは,i番目のサービスについて期間の総日数からすぬけプライムを使う日数分を引いた期間にCiを乗じてトータルをだし,最後にすぬけプライムを使う日数にCを乗じたコストを足す,という方針です。この方針は結局詰まって解ききれませんでしたが考える方向はよくて次につながりました。

 そこでひらめいたのはあるサービスについて使い始めた日以降はトータルのコストがCi増え,使い終わった翌日にはトータルのコストがCi減るので,各日のコストをtempとして各サービスの開始及び終わりに+Ci -Ciを入力した配列を準備することでした。

 コストの変更が生じるイベント毎に,各日のトータルコストtempがCを上回る場合にはCを乗じ,その他の場合にはtempを乗じます。

 vectorで実装したところサンプルが通ったので提出したところWA。
 同じ日に複数のイベントが発生した場合にバグるようです。

 そこで,同じ日に複数のイベントが発生した場合に新たなvectorに集約することを考えましたがなかなかうまくいきません。いろいろ試しているうちにイベントの発生する日をキーにするのだからmapを使えばいいのではないか,とひらめき,mapで実装。

 ACできました!

提出コード

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
#define rep(i, n) for (ll i = 0; i < n; i++)
#define all(v) v.begin(), v.end()
const ll MOD=1000000007;

int main(){
  ll n,c;
  cin>>n>>c;
  ll A[n];
  ll B[n];
  ll C[n];
  rep(i, n)cin>>A[i]>>B[i]>>C[i];
  vector<pair<ll,ll>>P;
  rep(i, n){
    P.push_back({A[i],C[i]});
    P.push_back({B[i]+1,-C[i]});
  }
  sort(all(P));
  ll temp=0;
  ll pre=0;
  map<ll,ll>mp;
  for(auto e:P){
    mp[e.first]+=e.second;
  }
  /*for(auto e:mp){
    cout<<e.first<<" "<<e.second<<endl;
  }
  */
    
  //すぬけ発動期間を考えながら金額を計算
  pre=0;
  ll ans=0;
  for(auto e:mp){
    if(pre==e.first) continue;
    //cout<<e.first<<" "<<e.second<<" "<<temp<<endl;    
    if(temp>=c){
      ans+=(c*(e.first-pre));
    }
    else{
      ans+=(temp*(e.first-pre));
    }
    temp+=e.second;
    pre=e.first;
  }
  cout<<ans<<endl;
  return 0;
}

E問題

 D問題を通して10分ほど時間があったので検討しましたが手が出ず。
 あとで解説を見ましたがまだよく理解できていません。

感想など

 最近はyukicoderやcodeforcesなどで比較的低難易度の問題を解くことをやっています。その中で,これまでうまく使えていなかった範囲for文とか,repマクロ,typedefなどのテンプレートを使えるようになってきており,基礎力向上に役立っているような気がします。

f:id:chacoder:20210111124434p:plain

f:id:chacoder:20210111124220p:plain


 D問題をあきらめずに解き切れたことは自信になりました。一時期,過去問の緑dif、水difなどを時間をかけて解き切ることをやっていたので、あきらめずに取り組めばそこそこのレベルの問題でも解けるはずなのですが,これまでは本番の雰囲気にのまれてあきらめてしまう弱さがありました。

 必ず解けると信じて粘ると,その間,確実に力がつくし,時には本当に解けることがあります。

 せっかくのコンテストなので自分に負荷をかけて力を伸ばす努力をこれからもしていきたいと思います。

息子に抜かれたこと

今回,ついに高校1年生の息子に抜かれてしましました。
しばらく前に激冷えして一瞬抜かれたことがありましたが,今回はE問題を解かれ水パフォ出しての堂々の入緑で抜かれたのでちょっと差をつけられました。

これまでは数学の基礎力とか精進量とかでなんとかカバーしていましたがそろそろ厳しい感じに。

f:id:chacoder:20210111124641p:plain

f:id:chacoder:20210111125311p:plain

f:id:chacoder:20210111125424p:plain

しかしこれで私を倒したとは思わないで欲しい。

息子は冬休みを終えて明日には帰寮するので,春休みまでの間にしっかり抜き返しておきたいなと思います。