F - Octopus Editorial by mechanicalpenciI
まず、頭を \(x=k_0\) においた時に問題文の条件をみたすように \(N\) 個の宝を掴むことができるか判定する方法について考えます。
頭から宝までの距離、すなわち \(\lvert X_i-k_0\rvert\) \((1\leq i\leq N)\) を昇順にソートした \((Y_1,Y_2, \ldots, Y_N)\) を考えます。
もしこれが任意の \(1\leq i\leq N\) について \(Y_i\leq L_i\) をみたしているならば条件を満たすように掴むことができます。これは、\(i\) 本目の足で頭から \(Y_i\) の距離にある宝を掴めば良いです。
一方、ある \(1\leq i\leq N\) について \(Y_i>L_i\) ならば条件を満たすように掴むことができません。なぜなら、少なくとも \((N-i+1)\) 個の宝は頭から \(L_i+1\) 以上離れたところにありますが、それをつかめる腕は高々 \((N-i)\) 本しか存在しないからです。
\((Y_1,Y_2,\ldots,Y_N)\) のソートはクイックソートなどで愚直に行って \(O(N\log N)\) で比較でき, さらに \(x=k_0\) に左右でそれぞれ最も近いものを比較していく方法をとることで \(O(N)\) で行えるため、 それぞれ \(O(N\log N)\) または \(O(N)\) で判定できます。
しかし、頭の候補として考えられる範囲は\(-2\times 10^{18}\leq x\leq 2\times 10^{18}\) であり、全てに対して時間内に判定を行うことはほぼ不可能と考えられます。そこで、頭の位置が \(x=k_0\) のときと \(x=k_0+1\) の時で条件をみたすかどうかが変わる可能性があるのはどういう時かについて考えます。
- \(x=k_0\) で条件をみたしていたが、\(x=k_0+1\) で条件をみたさなくなる場合
この場合、\(x=k_0\) のとき条件をみたしていた足と宝の対応が条件を満たさなくなる必要があります。つまりある \(1\leq i,j\leq N\) で, \(k_0-L_j\leq X_i\leq k_0+L_j\) かつ(\(X_i<k_0+1-L_j\) または \(k_0+1+L_j<X_i\))となる必要があります。このような条件をみたすのは、\(k_0\) が整数であることとあわせると、\(k_0=X_i+L_j\) であるような場合しかありません。よって、このような\(k_0\)は高々 \(N^2\) 個しか存在しません。
- \(x=k_0\) で条件をみたしていなかったが、\(x=k_0+1\) で条件をみたすようになる場合
この場合、\(x=k_0+1\) のとき条件をみたしていた足と宝の対応が条件を満たさなくなる必要があります。つまりある \(1\leq i,j\leq N\) で, \(k_0+1-L_j\leq X_i\leq k_0+1+L_j\) かつ(\(X_i<k_0-L_j\) または \(k_0+L_j<X_i\))となる必要があります。このような条件をみたすのは、\(k_0\) が整数であることとあわせると、\(k_0=X_i-L_j-1\) であるような場合しかありません。よって、このような\(k_0\) も高々 \(N^2\) 個しか存在しません。
よって、次のような集合 \(S=\{X_i+L_j|1\leq i,j\leq N \}\bigcup \{X_i-L_j-1|1\leq i,j\leq N \}\) を考えると、その要素数は高々 \(2N^2\) であり、その要素を昇順に \(S_1,S_2,\ldots,S_{|S|}\) \((S_1<S_2<\cdots<S_{|S|})\) とすると、 \(2\leq i\leq |S|\) について、\(x=S_i\) で問題文の条件をみたす事と、 \(S_{i-1}+1\leq x\leq S_i\) で問題文の条件をみたすことは同値であることがわかります。なお、\(x\leq S_1\), \(S_{|S|}+1\leq x\) の範囲においては、十分遠方に頭がある場合問題文の条件を絶対にみたさないことからこの範囲全体において条件をみたさないことがわかります。
よって、\(2\leq i\leq |S|\) について、\(x=S_i\) のときに問題文の条件をみたすかを判定すれば十分です。
\(S\) の要素の列挙およびソートに \(O(N^2\log N)\), 各要素に対する判定に合計で \(O(N^3)\) (あるいは \(O(N^3\log N)\) ) であるため、この問題を十分高速に解くことができました。
c++ による実装例:
#include <bits/stdc++.h>
using namespace std;
int n;
vector<long long>l,x;
bool judge(long long k){
int le=-1,ri=0,nx;
for(int i=0;i<n;i++){
if(x[i]<k)le++,ri++;
else break;
}
for(int i=0;i<n;i++){
if(le<0)nx=ri,ri++;
else if(ri>=n)nx=le,le--;
else if((k-x[le])>(x[ri]-k))nx=ri,ri++;
else nx=le,le--;
if(abs(x[nx]-k)>l[i])return false;
}
return true;
}
int main(void) {
long long val,ans=0;
vector<long long>s;
cin>>n;
for(int i=0;i<n;i++){
cin>>val;
x.push_back(val);
}
for(int i=0;i<n;i++){
cin>>val;
l.push_back(val);
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
s.push_back(x[i]+l[j]);
s.push_back(x[i]-l[j]-1);
}
}
sort(s.begin(),s.end());
int sz=s.size();
for(int i=1;i<sz;i++){
if(judge(s[i]))ans+=s[i]-s[i-1];
}
cout<<ans<<endl;
return 0;
}
posted:
last update: