E - Cover query 解説 by en_translator
Consider managing the intervals formed by the cells painted white in an ordered set.
For example, when cells \( 1,2,3,4,5, 8,9,10, 14\) are painted white, we manage them like \(\{[1,5], [8,10], [14,14]\}\).
The initial state is \([1,N]\).
Suppose that the state before a query is \(\{[l_1,r_1], [l_2,r_2], \ldots, [l_k,r_k]\}\) \((l_1\leq r_1<l_2\leq r_2<\cdots<l_k\leq r_k)\).
When we are given a query \((L,R)\) (we omitted subscripts for simplicity), how can we update them?
This can be done as follows.
- If \(l_i\leq r_i<L\), the interval remains as is.
- If \(l_i<L\) and \(L\leq r_i\leq R\), the interval \([l_i,r_i]\) is removed, and a new interval \([l_i,L-1]\) is inserted instead.
- If \(l_i<L\) and \(R<r_i\), the interval \([l_i,r_i]\) is removed, and new intervals \([l_i,L-1]\) and \([R+1,r_i]\) are inserted instead.
- If \(L\leq l_i \leq R\) and \(r_i\leq R\), the interval \([l_i,r_i]\) is removed.
- If \(L\leq l_i \leq R\) and \(R<r_i\), the interval \([l_i,r_i]\) is removed, and a new interval \([R+1,r_i]\) is inserted.
- If \(R<l_i\leq r_i\), the interval remains as is.
Here, the second case occurs for at most one \(i\) for each query, because there is at most one \(i\) such that \(l_i<L\leq r_i\). The third and fifth case also occurs for at most one \(i\).
Also, the number of intervals increases only by the third case, for every query increases the number of intervals by at most one.
However, if you update all intervals for each query, it costs \(O(Q^2)\) time, exceeding the execution time limit.
Instead, we will avoid updates for those untouched—the first and sixth cases. Here, note that if \(j<i\) and \(r_i<L\), then \(r_j<L\); likewise, if \(i<j\) and \(R<l_i\), then \(R<l_j\). Therefore, we see that there exists an integer pair \((i_0,i_1)\) such that any interval that falls into the second, third, fourth, or fifth case can be written as \([l_i,r_i]\) \((i_0\leq i\leq i_1)\). (For \(i<i_0\), interval \([l_i,r_i]\) falls into the first case; for \(i>i_1\), into the sixth.)
Given \(\{[l_1,r_1], [l_2,r_2], \ldots, [l_k,r_k]\}\), we need to obtain the minimum \(I\) with \(L\leq r_i\). This can be done with an appropriate function (like lower_bound against std::set for C+, and bisect_left for SortedSet in Python). Afterward, while \(l_i\leq R\) we increment \(i\), and terminate it once \(R<l_i\). This way, we can perform updates to those only requiring updates (the second through fifth cases).
Now consider the number of operations required. As mentioned above, the second, third, and fifth case occurs at most once per query. Meanwhile, the fourth case reduces the number of interval by one; together with the aforementioned fact “every query increases the number of intervals by at most one,” it turns out that the fourth case happens at most \((Q+1)\) times (in fact, \(Q\) times). Hence, the total number of operations is \(O(Q)\) over all queries; since searching, insertion, and removal can be done in \(O(\log Q)\) time (because the size of the set is bounded by \(Q\)), the problem can be solved in a total of \(O(Q\log Q)\) time, which is fast enough under the constraints of the problem.
To report the answers, maintain another variable managing the total number of white cells, and update it every time you modify the set, and print the value after the updates for each query end.
When implementing, the idea of regarding cell \(i\) as an interval covering \([i,i+1)\) facilitates index handling (although it is not adopted in the sample code below).
Sample code in C++:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n,q,L,R;
set<pair<int,int> >st; //(r_i,l_i)
set<pair<int,int> >::iterator itr;
cin>>n>>q;
st.insert({n,1});
int ans=n;
for(int i=0;i<q;i++){
cin>>L>>R;
itr=st.lower_bound({L,-1}); //the smallest i s.t. L<=r_i
vector<pair<int,int> >resv;
while(itr!=st.end()){
int sl=(*itr).second;
int sr=(*itr).first;
if(R<sl)break;
if((sl<L)&&(R<sr)){
resv.push_back({L-1,sl});
resv.push_back({sr,R+1});
}
else if((sl<L)&&(L<=sr)){
resv.push_back({L-1,sl});
}
else if((R<sr)&&(sl<=R)){
resv.push_back({sr,R+1});
}
ans-=min(sr,R)-max(sl,L)+1;
itr=st.erase(itr);
}
int sz =resv.size();
for(int i=0;i<sz;i++){
st.insert(resv[i]);
}
cout<<ans<<endl;
}
return 0;
}
投稿日時:
最終更新: