Official
E - フェンスの塗装 / Painting the Fence Editorial
by
E - フェンスの塗装 / Painting the Fence Editorial
by
physics0523
Editorial for printing
座標圧縮を利用して \(N\) 個の板ではなく \(O(M)\) 個の板の区間として管理します。
例えば \(9\) 枚の板のうち \([3,6]\) を塗る指示、 \([2,5]\) を塗る指示があるとすれば、板 \([1,1],[2,2],[3,5],[6,6],[7,9]\) という板の区間に着目すれば十分です(区間内について、塗られたかどうかの結果が必ず一致します)。
また、各板について、それが最初に塗られる指示を \(l\) 回目、最後に塗られる指示を \(r\) 回目とします。
この問題では連続する \(K\) 回の指示を無視するので、この板が 塗られない 必要十分条件は \(l\) 回目と \(r\) 回目の指示が共に無視されることです。
この \(l,r\) から、 \(i\) 回目から \(i+K-1\) 回目の指示を無視する時にその板が塗られなくなるような \(i\) の区間が求まります。
このことから、以下の解法が導出されます。
- 各板の区間について、それが最初に塗られる指示と最後に塗られる指示を求める。
- \(i\) 回目から \(i+K-1\) 回目の指示を無視したときにその区間が塗られないという \(i\) の区間に対して、何枚の板が塗られないかを imos 法の要領で記録する。
- 最後に、上記の情報を集計して、塗られない板の枚数が最も少ないような無視のしかたを求める。
時間計算量は \(O(M \log M)\) です。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
int main(){
int N,M,K;
cin >> N >> M >> K;
vector<int> L(M),R(M);
map<int,int> mp;
for(int i=0;i<M;i++){
cin >> L[i] >> R[i];
L[i]--;
mp[L[i]]=0;
mp[R[i]]=0;
}
mp[0]=0;
mp[N]=0;
vector<int> pos;
for(auto &nx : mp){
nx.second=pos.size();
pos.push_back(nx.first);
}
int cnt=pos.size();
for(auto &nx : L){nx=mp[nx];}
for(auto &nx : R){nx=mp[nx];}
vector<int> low(cnt,1e9),high(cnt,-1e9);
set<int> st;
for(int i=0;i<cnt;i++){st.insert(i);}
for(int i=0;i<M;i++){
int l=L[i];
int r=R[i];
while(true){
auto it=st.lower_bound(l);
if(it==st.end()){break;}
if((*it)>=r){break;}
low[(*it)]=i;
st.erase(it);
}
}
for(int i=0;i<cnt;i++){st.insert(i);}
for(int i=M-1;i>=0;i--){
int l=L[i];
int r=R[i];
while(true){
auto it=st.lower_bound(l);
if(it==st.end()){break;}
if((*it)>=r){break;}
high[(*it)]=i;
st.erase(it);
}
}
vector<int> bk(M+1,0);
for(int i=0;i+1<cnt;i++){
int del=pos[i+1]-pos[i];
if(low[i]>high[i]){
bk[0]+=del;
continue;
}
int lef=max(0,high[i]-K+1);
int rig=low[i];
if(lef<=rig){
bk[lef]+=del;
bk[rig+1]-=del;
}
}
int sub=1.1e9;
for(int i=0;i<=M-K;i++){
if(i){bk[i]+=bk[i-1];}
sub=min(sub,bk[i]);
}
cout << N-sub << "\n";
return 0;
}
posted:
last update:
