LINK:跳跃
不算难想的题目 考试的时候没想出来 还是想的太少 思路被束缚住了。
第一个想法 二分 发现check的时候还是需要枚举点对来算距离什么的 然后弃掉。
计算过样例后发现一个点到达右边可能先去左边再一下子跳到右边。
直接建图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的 放弃治疗。
还是考虑二分 二分出答案之后 我们需要得到两个点对之间跳mid步都达不到对方。
设(L_{i,j})表示i这个点跳j步所能到达最左端的地方。
容易发现这个东西可能可以反复横跳 所以还需要一个数组 (R_{i,j})表示i这个点跳j步所能达到最右端的地方。
转移显然。
现在有两个问题需要解决一个是L R数组如何快速求出 一个是如何判定答案。
考虑前者 容易想到倍增。正确性显然 这样就可以在nlog^2的时间内求了 注意利用线段树维护 好写一点。
考虑后者 如果答案还可以更大的话 那么必然两个点x,y(x<y) 满足x跳mid步到最右端<y y跳mid步到最左端>x.
这样check即可。
不过可以发现对于二分一个mid,log.我们需要先用倍增数组拼成mid log.拼一次nlog.
复杂度nlog^3. 一个小trick 直接倍增求答案 然后这样就省掉了二分。复杂度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;
}