リンク:ジャンプ
質問について考えることはそれほど難しくありませんが、私はそれについてあまり考えませんでした。
最初のアイデアは、チェックするときに、ポイントペアを列挙して距離を計算し、それを破棄する必要があるということです。
サンプルを計算した後、ポイントが右に到達した場合は、左に移動してから右にジャンプできます。
bfsを直接ビルドします。
これはn ^ 3の直接線ツリー最適化マッピングであることがわかります。複雑さn ^ 2log。
const int MAXN=3010;
int n,maxx,len,cnt,root,id;
int a[MAXN];
struct wy{int l,r;}t[MAXN<<2];
int b[MAXN][MAXN],vis[MAXN*MAXN],dis[MAXN*MAXN];
int lin[MAXN*MAXN],ver[MAXN*MAXN],nex[MAXN*MAXN],e[MAXN*MAXN];
inline void add(int x,int y,int z)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
e[len]=z;
}
inline void build(int &p,int l,int r)
{
if(!p)p=++cnt;
if(l==r){add(p,l,1);return;}
int mid=(l+r)>>1;
build(l(p),l,mid);
build(r(p),mid+1,r);
add(p,l(p),0);add(p,r(p),0);
}
inline void change(int p,int l,int r,int L,int R,int x)
{
if(L<=l&&R>=r)
{
add(x,p,0);
return;
}
int mid=(l+r)>>1;
if(L<=mid)change(l(p),l,mid,L,R,x);
if(R>mid)change(r(p),mid+1,r,L,R,x);
return;
}
deque<int> q;
inline void bfs(int w)
{
++id;q.pf(w);dis[w]=0;vis[w]=id;
while(q.size())
{
int x=q.front();
q.popf();
go(x)
{
if(vis[tn]!=id)
{
vis[tn]=id;
dis[tn]=dis[x]+e[i];
if(!e[i])q.pf(tn);
else q.pb(tn);
}
}
}
}
int main()
{
//freopen("jumping.in","r",stdin);
//freopen("jumping.out","w",stdout);
get(n);
if(n<=3000)
{
cnt=n;build(root,1,n);
for(int i=1;i<=n;++i)
{
int L,R;get(a[i]);
L=max(i-a[i],1);
R=min(i+a[i],n);
change(root,1,n,L,R,i);
}
rep(1,n,i)
{
bfs(i);
rep(1,n,j)b[i][j]=dis[j];
}
int ans=0;
rep(1,n,i)rep(1,i,j)ans=max(ans,min(b[i][j],b[j][i]));
put(ans);return 0;
}
return 0;
}
ポイントペアを列挙する以外に良い方法はないことがわかりました。残りの時間は、無向グラフの直径/位相グラフの直径を調べることです。複雑さの下限はnmであり、扱いをあきらめます。
2点と2点の回答を検討した後、2点間の中間点ジャンプを取得する必要があり、互いに到達できません。
セット\(L_ {I、J} \) Iホップステップjは左端の場所に到達することができ表します。
このことは繰り返しジャンプできる可能性があることは簡単にわかるので、配列\(R_ {i、j} \)は、jステップジャンプして右端に到達できることを示すために必要です。
転送は明らかにです。
ここで解決する必要のある2つの問題があります。1つは、LR配列に対する答えをすばやく見つける方法です。
前者を考えると、掛け算を考えるのは簡単です。正確さは明らかに、線分ツリーを使用してnlog ^ 2の時点で適切な書き込みを維持するように注意を払うことができるようにするためです。
後者を考慮すると、答えが大きくなる可能性がある場合は、xを満足する2つの点x、y(x <y)があり、中間ステップを右端にジャンプする<yy中間ステップを左端にジャンプする> xでなければなりません。
この方法で確認してください。
しかし、中間の半分の場合は対数であることがわかります。最初に乗数配列を使用して中間対数を綴る必要があります。nlogを1回入力します。
Complexity nlog ^ 3.小さなトリックが答えを直接乗算し、2つのポイントを保存します。複雑さnlog ^ 2。
答えを決定するとき、あなたは厳格でなければなりません>そして<それが厳格でないならば、それは境界または境界などのいくつかの部分より多いかもしれません。
const int MAXN=200010;
int n,ans;
struct wy{int l,r,sum;}t[MAXN<<2];
int a[MAXN],Log[MAXN],w[MAXN],bl[MAXN],br[MAXN],c[MAXN];
int L[MAXN][20],R[MAXN][20],ansl[MAXN],ansr[MAXN],wl[MAXN],wr[MAXN];
inline void build(int p,int l,int r)
{
l(p)=l;r(p)=r;
if(l==r){sum(p)=w[l];return;}
int mid=(l+r)>>1;
build(zz,l,mid);
build(yy,mid+1,r);
sum(p)=min(sum(zz),sum(yy));
}
inline int ask(int p,int l,int r)
{
if(l<=l(p)&&r>=r(p))return sum(p);
int mid=(l(p)+r(p))>>1;
if(r<=mid)return ask(zz,l,r);
if(l>mid)return ask(yy,l,r);
return min(ask(zz,l,r),ask(yy,l,r));
}
inline int check()
{
fep(n,1,i)c[i]=max(c[i+1],wl[i]);
rep(1,n,i)if(wr[i]!=n&&c[wr[i]+1]>i)return 1;
return 0;
}
int main()
{
freopen("1.in","r",stdin);
get(n);Log[0]=-1;
rep(1,n,i)get(a[i]),ansl[i]=ansr[i]=i,L[i][0]=max(1,i-a[i]),R[i][0]=min(n,i+a[i]),Log[i]=Log[i>>1]+1;
rep(1,Log[n],j)
{
rep(1,n,i)w[i]=L[i][j-1];
build(1,1,n);
rep(1,n,i)L[i][j]=ask(1,L[i][j-1],R[i][j-1]);
rep(1,n,i)w[i]=-R[i][j-1];
build(1,1,n);
rep(1,n,i)R[i][j]=-ask(1,L[i][j-1],R[i][j-1]);
}
fep(Log[n],0,j)//形成答案集合.
{
rep(1,n,i)bl[i]=ansl[i],br[i]=ansr[i];
rep(1,n,i)w[i]=L[i][j];
build(1,1,n);
rep(1,n,i)wl[i]=ask(1,bl[i],br[i]);
rep(1,n,i)w[i]=-R[i][j];
build(1,1,n);
rep(1,n,i)wr[i]=-ask(1,bl[i],br[i]);
if(check())
{
rep(1,n,i)ansl[i]=wl[i],ansr[i]=wr[i];
ans=ans|(1<<j);
}
}
put(ans+1);
return 0;
}