Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
Reputeless committed Apr 30, 2022
1 parent ac810cd commit 60308ce
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 1 deletion.
240 changes: 240 additions & 0 deletions 029.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
# 029 - Long Bricks (★5)

## 解答

```cpp
#include <iostream>
#include <vector>
#include <algorithm> // std::max()

// 遅延評価セグメント木
class LazySegmentTree
{
public:

LazySegmentTree() = default;

explicit LazySegmentTree(size_t size)
{
// size 以上である 2 のべき乗数を求めて m_size に代入
while (m_size < size)
{
m_size *= 2;
}

m_nodes.resize((m_size * 2 - 1), 0);
m_lazyNodes.resize((m_size * 2 - 1), 0);
}

// 区間 [begin, end) を value に更新する.
void update(size_t begin, size_t end, int value)
{
update(begin, end, value, 0, 0, m_size);
}

// 区間 [begin, end) における最大値を求める
int rangeMax(size_t begin, size_t end)
{
return rangeMax(begin, end, 0, 0, m_size);
}

private:

// si: ノードのインデックス
void propagate(size_t si)
{
if (m_lazyNodes[si])
{
m_nodes[si] = m_lazyNodes[si];

// 最下段でなければ子ノードに伝搬
if (si < (m_size - 1))
{
m_lazyNodes[si * 2 + 1] = std::max(m_lazyNodes[si * 2 + 1], m_lazyNodes[si]);
m_lazyNodes[si * 2 + 2] = std::max(m_lazyNodes[si * 2 + 2], m_lazyNodes[si]);
}

m_lazyNodes[si] = 0;
}
}

// 区間 [begin, end) を value に更新する.
// si: 確認するノードのインデックス. そのノードに対応する区間は [sBegin, sEnd)
void update(size_t begin, size_t end, int value, size_t si, size_t sBegin, size_t sEnd)
{
propagate(si);

// 更新するノードの区間と交差しない場合
if ((sEnd <= begin) || (end <= sBegin))
{
return;
}
// 更新するノードの区間が完全に含まれている場合
if ((begin <= sBegin) && (sEnd <= end))
{
m_lazyNodes[si] = value;
propagate(si);
return;
}

update(begin, end, value, (si * 2 + 1), sBegin, (sBegin + sEnd) / 2);
update(begin, end, value, (si * 2 + 2), (sBegin + sEnd) / 2, sEnd);
m_nodes[si] = std::max(m_nodes[si * 2 + 1], m_nodes[si * 2 + 2]);
}

// 区間 [begin, end) における最大値を求める.
// si: 確認するノードのインデックス. そのノードに対応する区間は [sBegin, sEnd)
int rangeMax(size_t begin, size_t end, size_t si, size_t sBegin, size_t sEnd)
{
propagate(si);

// 確認するノードの区間と交差しない場合
if ((sEnd <= begin) || (end <= sBegin))
{
return 0;
}

// 確認するノードの区間が完全に含まれている場合
if ((begin <= sBegin) && (sEnd <= end))
{
return m_nodes[si];
}

const int lc = rangeMax(begin, end, (si * 2 + 1), sBegin, (sBegin + sEnd) / 2);
const int rc = rangeMax(begin, end, (si * 2 + 2), (sBegin + sEnd) / 2, sEnd);
return std::max(lc, rc);
}

private:

size_t m_size = 1;

// 計算済みの配列
std::vector<int> m_nodes;

// 遅延評価用の配列
std::vector<int> m_lazyNodes;
};

int main()
{
// W 個の正方形のマス, N 個のレンガ
int W, N;
std::cin >> W >> N;

LazySegmentTree segTree(W);

// それぞれのレンガについて
for (int i = 0; i < N; ++i)
{
// 左から L 番目 ~ R 番目を覆う
int L, R;
std::cin >> L >> R;

// 区間をを [L, R) で表現する
// 1 番目 ~ 3 番目の場合 [0, 3)
--L;

// 対象区間の現在の高さ
const int oldH = segTree.rangeMax(L, R);
// 対象区間の新しい高さ (+1)
const int newH = (oldH + 1);

// 対象区間の高さを更新する
segTree.update(L, R, newH);

// 高さを出力
std::cout << newH << '\n';
}
}
```
## 部分点解答 (座標圧縮)
座標圧縮による解法で、小課題 1, 2 を解けます。
```cpp
#include <iostream>
#include <vector>
#include <algorithm> // std::sort(), std::unique(), std::lower_bound(), std::max_element()
#include <iterator> // std::distance()
int main()
{
// W 個の正方形のマス, N 個のレンガ
int W, N;
std::cin >> W >> N;
// レンガの開始座標を記録する配列
std::vector<int> Ls(N);
// レンガの終端座標を記録する配列
std::vector<int> Rs(N);
// それぞれのレンガについて
for (int i = 0; i < N; ++i)
{
// 左から L 番目 ~ R 番目を覆う
int L, R;
std::cin >> L >> R;
// 区間を記録する
// 1 番目 ~ 3 番目の場合 [0, 3)
Ls[i] = (L - 1);
Rs[i] = R;
}
////////////////////////////////
//
// 座標圧縮
//
// 登場する座標を記録, ソート, 重複排除する
std::vector<int> coordinates;
for (int i = 0; i < N; ++i)
{
coordinates.push_back(Ls[i]);
coordinates.push_back(Rs[i]);
}
std::sort(coordinates.begin(), coordinates.end());
coordinates.erase(std::unique(coordinates.begin(), coordinates.end()), coordinates.end());
for (int i = 0; i < N; ++i)
{
// 元の座標をマッピング後のインデックスに置き換える
Ls[i] = static_cast<int>(std::distance(coordinates.begin(), std::lower_bound(coordinates.begin(), coordinates.end(), Ls[i])));
Rs[i] = static_cast<int>(std::distance(coordinates.begin(), std::lower_bound(coordinates.begin(), coordinates.end(), Rs[i])));
}
//
////////////////////////////////
// 各区間の現在の高さ
// 区間数は (coordinates.size() - 1)
std::vector<int> h(coordinates.size() - 1);
for (int i = 0; i < N; ++i)
{
// レンガを積む区間 [itLeft, itRight)
const auto itLeft = (h.begin() + Ls[i]);
const auto itRight = (h.begin() + Rs[i]);
// 対象区間の現在の高さ
const int oldH = *std::max_element(itLeft, itRight);
// 対象区間の新しい高さ (+1)
const int newH = (oldH + 1);
// 対象区間の高さを更新する
std::fill(itLeft, itRight, newH);
// 高さを出力
std::cout << newH << '\n';
}
}
```
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ C++17 標準ライブラリの機能を優先して使い、競技プログラ
|006| | | | |
|013| | | | |
|021| | | | |
|029| | | | |
|[029](https://atcoder.jp/contests/typical90/tasks/typical90_ac)|[Long Bricks](./029.md)|★5|(1) [👨‍🏫](https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/editorial/029-01.jpg) / [📝](https://github.com/E869120/kyopro_educational_90/blob/main/sol/029-01.cpp), [📝](https://github.com/E869120/kyopro_educational_90/blob/main/sol/029-02.cpp)<br>(2) [👨‍🏫](https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/editorial/029-02.jpg) / [📝](https://github.com/E869120/kyopro_educational_90/blob/main/sol/029-03.cpp)|(解法 1) 「座標圧縮」で効率化<br>(解法 2) 区間に対する処理は「セグメント木」|
|030| | | | |
|036| | | | |
|[037](https://atcoder.jp/contests/typical90/tasks/typical90_ak)|[Don't Leave the Spice](./037.md)|★5|[👨‍🏫](https://raw.githubusercontent.com/E869120/kyopro_educational_90/main/editorial/037.jpg) / [📝](https://github.com/E869120/kyopro_educational_90/blob/main/sol/037.cpp)|DP をセグメント木で高速化|
Expand Down

0 comments on commit 60308ce

Please sign in to comment.