题解:
这道题很妙啊。
显然是要二分之后每个位置找到前后第一个合法位置,分别记为 ,然后要求包含任意一个。
这个时候我们只需要找到 中间第一个非法的位置,然后递归下去做就好了,问题是怎么找到这个非法位置。
显然这是一个区间内的二维偏序,也就是三维偏序,可以KD树做或者线段树+主席树做,不过复杂度多了一个 ,这时候有个非常高明的方法:考虑一下暴力从两端往中间扫,复杂度为 ,总时间复杂度 ,加上二分,时间复杂度为 。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
class FindingFriends {
int n,lim,a[N],li[N],ri[N];
int m,mx[N<<4],vec[N<<4];
inline void build(int k,int l,int r,int t) {
mx[k]=t; if(l==r) return;
int mid=(l+r)>>1;
build(k<<1,l,mid,t);
build(k<<1|1,mid+1,r,t);
}
inline int ask(int k,int l,int r,int L,int R) {
if(L<=l && r<=R) return mx[k];
int mid=(l+r)>>1;
if(R<=mid) return ask(k<<1,l,mid,L,R);
else if(L>mid) return ask(k<<1|1,mid+1,r,L,R);
else return max(ask(k<<1,l,mid,L,R),ask(k<<1|1,mid+1,r,L,R));
}
inline void inc(int k,int l,int r,int p,int v) {
if(l==r) {mx[k]=v; return;}
int mid=(l+r)>>1;
(p<=mid) ? inc(k<<1,l,mid,p,v) : inc(k<<1|1,mid+1,r,p,v);
mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
inline bool solve(int l,int r) {
if(r-l+1<lim) return false;
for(int i=l,j=r;i<=j;i++,j--) {
if(li[i]<l && ri[i]>r) return solve(l,i-1)|solve(i+1,r);
if(li[j]<l && ri[j]>r) return solve(l,j-1)|solve(j+1,r);
} return true;
}
inline int id(int v) {return lower_bound(vec+1,vec+m+1,v)-vec;}
int L[N],R[N],I[N];
inline bool check(int k) {
m=0;
for(int i=1;i<=n;i++)
vec[++m]=a[i]-k, vec[++m]=a[i], vec[++m]=a[i]+k;
sort(vec+1,vec+m+1);
m=unique(vec+1,vec+m+1)-vec-1;
build(1,1,m,0);
for(int i=1;i<=n;i++) L[i]=id(a[i]-k), R[i]=id(a[i]+k), I[i]=id(a[i]);
for(int i=1;i<=n;i++) {
li[i]=ask(1,1,m,L[i],R[i]);
inc(1,1,m,I[i],i);
}
build(1,1,m,-n-1);
for(int i=n;i>=1;i--) {
ri[i]=-ask(1,1,m,L[i],R[i]);
inc(1,1,m,I[i],-i);
}
return solve(1,n);
}
public:
int shortestDistance(int len, vector<int> init, int A, int b, int c, int d, int e) {
n=len; lim=e;
for(int i=1;i<=init.size();i++) a[i]=init[i-1];
for(int i=init.size()+1;i<=n;i++) a[i]=((long long)a[i-1]*A+(long long)b*(i-1)+c)%d;
int l=0, r=1e9, ans;
while(l<=r) {
int mid=(l+r)>>1;
if(check(mid)) ans=mid, r=mid-1;
else l=mid+1;
} return ans;
}
};