Official

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: