AtCoder Beginner Contest 097 解説備忘録

#前書き
AtCoder Beginner Contest 097 を解いてみた．解けなかった問題の備忘録．

#B問題
[[問題はこちら]](https://abc097.contest.atcoder.jp/tasks/abc097_b)

**[ポイント]**
・for文を2回使うことを恐れすぎて余計に考えてしまったが，**for文の終了条件を適切に定めておけば別に良い**

・今回は$x \leqq 1000$という条件だったので，$2^9<1000<2^{10}$，$31^{2} < 1000 < 32^2$であるため，以下のように設定した．

以下はACのコード：

```main.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cmath>
#include <set>
#include <sstream>
#include <bitset>

#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define REP(i,n)  FOR(i,0,n)

typedef long long ll;

using namespace std;

int main() {
    
    int x;
    cin >> x;
    int ans=1;
    FOR(b, 2, 32){
        FOR(p, 2, 10){
            if((pow(b, p)>ans)&&(pow(b, p)<=x)){
                ans = pow(b, p);
            }
        }
    }
    
    cout << ans << endl;
    
    
    return 0;
}


```

#C問題

[[問題はこちら]](https://abc097.contest.atcoder.jp/tasks/arc097_a)

**[ポイント]**
・またまたfor文を2回使うことを恐れすぎて余計に考えてしまったが，**for文の終了条件を適切に定めておけば別に良い**．今回は$0 \leqq k \leqq 5$であることを活かす(出力する単語は絶対5文字以下となる)．


・文字列系は`vector`を使いこなせれば良さそう．
`vector`の使い方は[こちら](https://qiita.com/ofutonfuton/items/203a3ccda06aa2ab59f9)を参照．

以下はACのコード：

```main.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cmath>
#include <set>
#include <sstream>
#include <bitset>

#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define REP(i,n)  FOR(i,0,n)

typedef long long ll;

using namespace std;

int main() {
    
    string s;
    cin >> s;
    
    int k;
    cin >> k;
    
    int n = s.size();
    
    vector<string> vec;
    FOR(j, 1, min(n,5)+1){
        FOR(i, 0, n-j+1){
            vec.push_back(s.substr(i,j));
        }
    }
    
    sort(vec.begin(), vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());
    
    cout << vec[k-1] << endl;
    
    return 0;
}


```

#D問題
 
[[問題はこちら]](https://abc097.contest.atcoder.jp/tasks/arc097_b)

**[ポイント]**
・実はグラフの問題．いかにグラフに帰着できたか．

・$x_k$と$y_k$ ($1 \leqq k \leqq M$) を枝でつなぎ，$p_i$と$i$ ($1 \leqq i \leqq N$)が同じ木にあれば良い．

・木の作成の際にUnion-Find木を作成する．
https://qiita.com/ofutonfuton/items/c17dfd33fc542c222396 に詳しく解説した．

以下はACのコード：

```main.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

using namespace std;

struct UnionFind {
    vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2
    
    UnionFind(int N) : par(N) { //最初は全てが根であるとして初期化
        for(int i = 0; i < N; i++) par[i] = i;
    }
    
    int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
        if (par[x] == x) return x;
        return par[x] = root(par[x]);
    }
    
    void unite(int x, int y) { // xとyの木を併合
        int rx = root(x); //xの根をrx
        int ry = root(y); //yの根をry
        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま
        par[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
    }
    
    bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
        int rx = root(x);
        int ry = root(y);
        return rx == ry;
    }
};

int main() {
    int N, M;
    cin >> N >> M;
    vector<int> P(N);
    for(int i = 0; i < N; i++){
        cin >> P[i];
    }
    
    UnionFind tree(N);
    
    for(int i = 0; i < M; i++) {
        int x, y;
        cin >> x >> y;
        tree.unite(x-1, y-1); // x-1の木とy-1の木を併合する
    }
    
    int cnt = 0;
    for(int i = 0; i < N; i++) {
        if (tree.same(i, P[i]-1)) { //iとP[i]-1が同じ木に属するなら，カウンタに1追加
            cnt++;
        }
    }
    cout << cnt << endl;
    return 0;
}

```

#あとがき
精進します．