0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AtCoder Beginner Contest 202 備忘録

Last updated at Posted at 2021-05-24

結果
A:AC
B:AC
C:AC
D:時間切れ ⇒後日解説みてAC
E:未
F:未

A - Three Dice
image.png

スマートじゃないけど、力技で各目が出た時の逆値を返す関数を作って計算。
よくよく考えると、7から引くだけでええやん。。。
まぁあってるので良し。

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

int dice(int a){
    int ans;
    if(a==1){
        ans=6;
    }
    else if(a==2){
        ans=5;
    }
    else if(a==3){
        ans=4;
    }
    else if(a==4){
        ans=3;
    }
    else if(a==5){
        ans=2;
    }
    else if(a==6){
        ans=1;
    }
    return ans;
}

int main(void){
    int a,b,c;
    cin>>a>>b>>c;
    int ans=dice(a)+dice(b)+dice(c);
    cout<<ans<<endl;

}

B - 180°
image.png

文字列を180°回転させるやつ。
・文字をreverseでまず反転させて、
・6は9へ、9は6へreplaceで置き換える
わりと簡単。

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

int main(void){
    string S;
    cin>>S;
    reverse(S.begin(), S.end());
    long long num=S.size();
    for(int i=0;i<num;i++){
        if(S.at(i)=='6'){
            S.replace(i,1,"9");
        }
        else if(S.at(i)=='9'){
            S.replace(i,1,"6");
        }
    }
    cout<<S<<endl;
}

C - Made Up

image.png

問題文わかりづらい。
B配列のうち、C配列の番号だけ抽出し、A配列と一致するものの数を数えるってこと。
一致するものの個数を比較なので、map関数使えばいいかなと思いつく。

具体例の方が思いつきやすいので、下記のような配列で考える。
image.png
この場合のA,Bの個数をカウントした連想配列AA,BB'は、
image.pngimage.png
となる。(故あってBB’としておく)
題意より、必要となるのは配列Bの情報ではなく、BのうちのC番目のものだけ。
なのでBB’からC番目のものだけを抜き出す。
image.png
イメージとしてはこんな感じ。
この部分を

BB[B[C[i]]]++;

と表現。
i=1のとき
C[1]=2 ⇒ B[2]=3 ⇒BB[3]がインクリメントされる。(=1)
i=2のとき
C[2]=2 ⇒ B[2]=3 ⇒BB[3]がインクリメントされる。(=2)
こんな感じで、C要素番目のB配列が連想配列BBに格納されていく。
これでB配列からの抽出は完了するので、あとはA配列との比較をする。
どっちも1以上(=存在する)場合を抽出し、複数個数がある場合は、組み合わせを考慮して、掛け算して…とひたすら積算。
これでオッケー。

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

int main(void){
    long long N;
    long long A[110000];
    long long B[110000];
    long long C[110000];
    map<long long,long long>AA;
    map<long long,long long>BB;
    map<long long,long long>CC;    
    long long ans=0;

    cin>>N;
    for(int i=1;i<=N;i++){//1番目からN番目で数えた方が間違いなさそうなので、1~Nにする
        cin>>A[i];
        AA[A[i]]++;//A配列の要素の個数をカウント
    }
    for(int i=1;i<=N;i++){
        cin>>B[i];
    }
    for(int i=1;i<=N;i++){
        cin>>C[i];
        BB[B[C[i]]]++;//B配列のうち、C配列要素の番号の数だけカウント
    }    
    for(int i=1;i<=N;i++){//最大でもNまで
        if(AA[i]>0&&BB[i]>0){//同じ番号で同時に1以上がある場合が該当
            ans=ans+AA[i]*BB[i];//組み合わせを考慮して要素の数で掛け算し、積算
        }
    }   
    cout<<ans<<endl;
}

D - aab aba baa
image.png

A個のaとB個のbから作られる文字列について、辞書順でK番目を求める問題。
解説通り一番上の文字から考えると、解説通りに実施。
コンビネーションについてはおーばーふろー対策で二項定理を使うが後述。

↓参考解説
https://blog.hamayanhamayan.com/entry/2021/05/22/225424

まず組合せの考え方。
総文字数はA+B。
文字列の組合せは、
「どこにBを入れるか」
さえ決まれば、Aは勝手にきまるので、
A+B C B通りある。よって、順序も同じ数だけある。
ここでアタマがaだったと仮定する。
aをfixすると残りはA+B-1 C B通りある。先ほどと同じように、順序も同じだけある。
よって求めるK番目が1~ A+B-1 C B以内であれば、アタマaが真となる。
そしてさらに一個下の文字をaと仮定して、Kより高いか低いか…を繰り返していく。
逆にKがA+B-1 C Bより大きい場合は、アタマがbとなるが、その場合次のけたはb以下の検索になるので、少し変わる。
この場合、飛ばした分を引く必要があるのでKからA+B-1 C Bから引いて、次の順序大小を比較する。
言葉でかくとむずかしい。

以上以下の境界に気を付けて実装すれば、理屈は追える。

ただし、この後ネックになるがaCbの計算。
これまで分母/分子で計算してたがa=20とかあたりで、longlongでもケタがオーバーフローする。
なので、ここでコンビネーションの計算に二項定理、パスカルの三角形を使う。
https://www.studyplus.jp/386

・(x+y)^nを展開した時の各項の係数を考える。
 係数は「x,yを○○個取ったときの組合せがいくつあるか」で決まるので、
 例えばn=3の時は、x^2・yの組合せは、xxy,xyx,yxxの3通りだから、係数は3になる。
 ってのが二項定理
・二項定理の定数は、パスカルの3角形で表せる。n+1Ck=nCk−1+nCk ってやつ。
 これなら和計算だけでCを算出可能。
 これを実装したのが、comb関数。色々コード例あったので、参考にして行列じゃなくて直接数値入力⇒数値出力の形に変更。
image.png

コンビネーション計算は二項定理必須。
テキトーにやるとすぐ地球滅亡レベルの計算量になっちゃうんだなと理解。

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

long long comb(long long a, long long b){//二項定理から算出する関数
    vector<vector<long long> > v(a+1, vector<long long>(a+1));
    for(int i = 0;i <v.size(); i++){//パスカルの3角形の外側=1になる部分を定義
        v[i][0]=1;
        v[i][i]=1;
        }       
    for(int k = 1;k <v.size();k++){
        for(int j = 1;j<k;j++){
            v[k][j]=(v[k-1][j-1]+v[k-1][j]);//パスカルの3角形をチマチマ計算
            }
        }
    long long T=v[a][b];
    return T;
}

int main(void){
    int A,B;
    long long K;
  	cin>>A>>B>>K;
    long long t;
    t=comb(A+B-1,B);
    string S="";
  	long long j=A+B;
    
    for(int i=0;i<j;i++){
        t=comb(A+B-1,B);
        if(A==0){
            S=S+'b';
            B--;          
        }
        else if(B==0){
            S=S+'a';
            A--;
        }
        else{
          if(t>=K){
            S=S+'a';
            A--;
          }
          else{
            S=S+'b';
            B--;
            K=K-t;
          }
        }
      }
    cout<<S<<endl;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?