公式

F - Not All Covered 解説 by MtSaka


\(C_i\) を 城壁 \(i\) を守っている砲台の数 \((1 \leq i \leq N)\)とします。

このとき、\(C\) の最小値が答えとなります。\(C\) の最小値を \(m\) としたとき、\(m\) 個未満の砲台を破壊してもどの城壁 \(i\)\(C_i-m+1\) 個以上の砲台が守っています。これは必ず正なのでどの城壁も \(1\) 個以上の砲台に守られています。 逆に、\(C_j=m\) となる \(j\) について城壁 \(j\) を守っている \(m\) 個の砲台を全て破壊すると、城壁 \(j\) を守っている砲台は \(0\) 個になり、どの砲台にも守られてない城壁が存在する状態になります。よって、\(C\) の最小値が答えとなります。

入力から \(C\) を計算する方法について考えます。 \(C_i\)\(L_j \leq i \leq R_j\) を満たす \(j(1 \leq j \leq M)\) の個数と等しいです。これを言い換えると、初め \(C=(0,0,\ldots,0)\) とし、各 \(i(1 \leq i \leq M)\) について \(C_{L_i},C_{L_i+1},\ldots,C_{R_i}\)\(1\) を加算したものが求めるべき \(C\) です。このまま愚直に計算すると時間計算量が \(O(NM)\) となり、この問題の制約下では実行時間制限に間に合いません。

この処理は imos法 を用いることで高速化できます。以下のような手順で \(C\) を計算することが可能です。また、便宜上 \(C\) は長さ \(N+1\) であるとします。

  • \(C=(0,0,\ldots,0)\) と初期化する
  • \(i=1,2,\ldots,M\) の順で以下を行う
    • \(C_{L_i} \leftarrow C_{L_i}+1\)
    • \(C_{R_i+1} \leftarrow C_{R_i}-1\)
  • \(i=2,\ldots,N+1\) の順で以下を行う
    • \(C_{i} \leftarrow C_{i}+C_{i-1}\)

\(C_1,C_2,\ldots,C_N\) の最小値が答えです(\(C_{N+1}\) は常に \(0\) になります)。

よって時間計算量 \(\mathrm{O}(N+M)\) でこの問題を解くことができます。

実装例(C++)

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    vector<int> imos(n + 1);
    for (int i = 0; i < m; ++i) {
        int l, r;
        cin >> l >> r;
        l--;
        imos[l]++, imos[r]--;
    }
    for (int i = 1; i <= n; ++i)
        imos[i] += imos[i - 1];
    int ans = 1e9;
    for (int i = 0; i < n; ++i)
        ans = min(ans, imos[i]);
    cout << ans << endl;
}

実装例(Python)

n, m = map(int, input().split())
imos = [0] * (n + 1)
for _ in range(m):
    l, r = map(int, input().split())
    l -= 1
    imos[l] += 1
    imos[r] -= 1
for i in range(1, n + 1):
    imos[i] += imos[i - 1]
ans = 1e9
for i in range(n):
    ans = min(ans, imos[i])
print(ans)

投稿日時:
最終更新: