いい感じの教育的典型問題。いもす法したあとに累積和する!
問題概要
個のマス がある。 個の区間があって、それぞれマス からマス までを連続して覆っている。 個の区間によって一回以上被覆されるマスの集合を とする。
各区間について、その区間を除外してできる残りの 個の区間によって被覆されるマスの集合が であるかどうかを判定せよ。
制約
考えたこと
まず、各マスが 個の区間によって何回被覆されるかをいもす法によって求めよう!
num[x]
:マス が何個の区間によって被覆されるか
このとき、 個の区間について、次の判定が高速にできればよい。
区間 内に、num[x] < 2
となるようなマス が存在しないとき、その区間は除外可能である
これを判定するためには、累積和が使える。つまり、
sum[x]
:左から マス分について、num
の値が 2 未満であるようなものの個数
を求めよう。そうすれば、sum[T[i]] - sum[S[i]]
の値が 0 であるかどうかを判定すればよいこととなる。
全体として、 の計算量で解ける。
コード
#include <bits/stdc++.h> using namespace std; int main() { // 入力と imos 法 int N, M; cin >> N >> M; vector<int> num(N+1, 0), S(M), T(M); for (int i = 0; i < M; i++) { cin >> S[i] >> T[i]; S[i]--; num[S[i]]++; num[T[i]]--; } for (int i = 0; i < N; i++) num[i+1] += num[i]; // 区間内の、num[x] = 1 となる x の個数を求めるための累積和配列 vector<int> sum(N+1, 0); for (int i = 0; i < N; i++) { int add = 0; if (num[i] < 2) add = 1; sum[i+1] = sum[i] + add; } // 各区間を考える vector<int> res; for (int i = 0; i < M; i++) { int violent_num = sum[T[i]] - sum[S[i]]; if (violent_num == 0) res.push_back(i); } cout << res.size() << endl; for (auto v : res) cout << v+1 << endl; }