---
tags: APIO
---
# APIO 2020-A 壁の塗装 (Painting Walls)
誤読に注意
## 問題文
https://apio2020.toki.id/assets/statements/paint_ja.pdf
https://oj.uz/problem/view/APIO20_paint
壁があり、 $N$ 個の領域に分かれている。 $i$ 番目の壁を色 $C_i$ で塗りたい。
そこで、 $M$ 人の作業員からなる業者に依頼することにした。
各作業員には好きな色がいくつかあり、好きな色しか塗ってくれない。
あなたは、 1 回の指示で以下のことができる。
- 整数 $0 ≤ x < M,\ 0 ≤ y ≤ N − M$ を選ぶ
- 各 $0 ≤ l < M$ について、作業員 $(x + l) \bmod M$ が領域 $y + l$ に色 $C_{y + l}$ を塗る
- ただし、好きな色でないため塗ってくれない作業員がいる場合、選べない。
すべての壁を塗るのに必要な最小の指示回数はいくつか?
$N ≤ 10^5$
$M ≤ 5 \times 10^4$
$\sum($色 $i$ が好きな人の数$)^2\;≤4\times10^5$
以下 $F=\sum($色 $i$ が好きな人の数$)^2\;≤4\times10^5$ とする。
## 考察
謎の制約 $\sum($色 $i$ が好きな人の数$)^2\;≤4\times10^5$ について考える。
色を 1 種類にすると $($色 $i$ が好きな人の数$)^2\;≤4\times10^5$ であるから、
$($色 $i$ が好きな人の数$)\;≤632$ がわかり、
$\sum($色 $i$ が好きな人の数$)\;≤632\times N≤6.32\times10^7$ となる。
<br>
<span style="font-size:90%">
色 $i$ が好きな人の数 ができるだけ均等になったほうが 2 乗和が小さいので、
$\sum($色 $i$ が好きな人の数$)\;≤N\sqrt\frac FN≤2\times10^5$ とするといい評価が得られる。
</span>
塗り方について考える。
$x=0,y=0$ のとき、作業員 $1$ が領域 $1$ を、作業員 $2$ が領域 $2$ を、作業員 $M - 1$ が領域 $M - 1$ を塗る。
$x=1,y=1$ のとき、作業員 $2$ が領域 $2$ を、作業員 $M - 1$ が領域 $M - 1$ を、作業員 $M$ が領域 $M$ を塗る。
このようになっているから、 $\text{paint}[i][j]=\;($領域 $j$ を作業員 $i-j\bmod M$ が塗れるか$)$ を用意すると、
ある $i$ が存在して $\text{paint}[i][j:j+M]$ が全て true のとき区間 $[j:j+M]$ を一度に塗れる ということがわかる。
$\text{paint}[i][j]$ はそのままだと $\Theta(NM)$ だが、 true なところだけ持つと $O(\sqrt{NF})$ になる。
あとは、位置 $0$ から始めて、その位置を含んでできるだけ遠くまで塗れる区間を選んで塗っていくと指示回数がわかる。
## 実装
TL が若干きつい。
$\text{paint}[i]=\;($連続して true な区間$)$ として持ってあげて、平面走査のようにすると速い。
https://oj.uz/submission/305643
```cpp
#include <bits/stdc++.h>
using namespace std;
void chmin(int& a, int b){ if(a > b) a = b; }
void chmax(int& a, int b){ if(a < b) a = b; }
int minimumInstructions(int N, int M, int K, vector<int> C, vector<int> A, vector<vector<int>> B){
vector range(M, pair{-1, -1});
vector next(N, 0);
vector trB(K, vector<int>{});
for(int i = 0; i < M; i++) for(int c : B[i]) trB[c].push_back(i);
for(int i = N; i--; ){
const int c = C[i];
for(int b : trB[c]){
int x = (b - i) % M;
if(x < 0) x += M;
auto& [l, r] = range[x];
if(l != i + 1) r = i + 1;
l = i;
if(r - l >= M) chmax(next[i], r);
}
}
for(int i = 0; i < N - 1; i++) chmax(next[i + 1], next[i]);
for(int i = 0; i < N; i++) chmin(next[i], i + M);
int ans = 0, at = 0;
while(at < N){
if(next[at] <= at) return -1;
at = next[at];
ans++;
}
return ans;
}
```