公式

E - Festival 解説 by PCTprobability


それぞれの日に対して独立に答えを求める \(\mathrm{O}(N\log N)\) 解法と、次の日の答えを用いて上手く今日の答えを求める \(\mathrm{O}(N)\) 解法を解説します。

\(\mathrm{O}(N\log N)\) 解法

それぞれの \(i(1 \le i \le N)\) に対して、\((A_1,A_2,\dots,A_M)\) のうち \(i\) 以上の最小の値を求められれば良いです。

まず、「\(B_i = i\) 日目までに花火が上がる回数」という配列 \(B\) を累積和を用いて求めておくことで、\(l\) 日目から \(r\) 日目の間に花火が上がるか?を \(B_r - B_{l-1} \ge 1\) かどうかで \(\mathrm{O}(1)\) で判定できるようにしておきます。

すると、それぞれの \(i\) に対して二分探索を行うことで \(i\) 日目から \(x\) 日目までで花火が \(1\) 回以上上がる最小の \(x\)\(\mathrm{O}(\log N)\) で求めることが出来ます。この \(x\)\(i\) 日目以降で初めて花火が上がる日となります。よって、各 \(i\) に対してこれを行うことでこの問題を \(\mathrm{O}(N\log N)\) で解くことが出来ます。

もしくは、C++ であれば lower_bound を用いて直接求めることも出来ます。

\(\mathrm{O}(N)\) 解法

さて、まず \(N\) 日目に対しては答えは \(0\) 日後です。(\(N\) 日目には花火が上がることが保証されているため。)

ここで、\(i+1\) 日目の答えを \(x\) とした上で \(i\) 日目の答えを考えます。もし \(i\) 日目に花火が上がるのであれば答えは \(0\) 日後です。そうでないならば、答えは \(x+1\) 日後となります。

よって、上記を \(i=N-1,N-2,\dots,2,1\) 順に行うことで \(\mathrm{O}(N)\) で全ての \(i\) に対して答えを求めることが出来ます。

実装例(C++・\(\mathrm{O}(N\log N)\)・二分探索解)

#include <bits/stdc++.h>
using namespace std;
int main(){
  int n,m;
  cin>>n>>m;
  vector<int> a(m);
  for(int i=0;i<m;i++){
    cin>>a[i];
    a[i]--;
  }
  vector<int> b(n+1);
  for(int i=0;i<m;i++) b[a[i]+1]++;
  for(int i=1;i<=n;i++) b[i]+=b[i-1];
  for(int i=0;i<n;i++){
    int ok=n,ng=i-1;
    while(abs(ok-ng)>1){
      int mid=(ok+ng)/2;
      if(b[mid+1]-b[i]>=1) ok=mid;
      else ng=mid;
    }
    cout<<ok-i<<endl;
  }
}

実装例(C++・\(\mathrm{O}(N\log N)\)・lower_bound 解)

#include <bits/stdc++.h>
using namespace std;
int main(){
  int n,m;
  cin>>n>>m;
  vector<int> a(m);
  for(int i=0;i<m;i++){
    cin>>a[i];
    a[i]--;
  }
  for(int i=0;i<n;i++){
    int x=*lower_bound(a.begin(),a.end(),i);
    cout<<x-i<<endl;
  }
}

実装例(C++・\(\mathrm{O}(N)\) 解)

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

投稿日時:
最終更新: