题解 P7599 [APIO2021] 雨林跳跃
(下文中
对于每个点左右可跳到的点直接二分+
先抛开最优解要求,我们思考如何构造一组合法解。取出
那么,有解的充要条件就是
根据上述策略构造出的解不一定是最优的,我们来思考如何优化我们的策略。我们优先考虑
我们将每个点可以跳到的更高的柱子视为其父亲,上述策略第一步就相当于树上倍增。而第二步则是序列上倍增。用两个倍增数组即可实现。这一步的时间复杂度是
接下来,我们将问题扩展到
最后,我们只需要将问题扩展到
综合上面所有的写法即可拿到满分,总时间复杂度
(赛时调试了不少时间,代码写得很丑,读起来可能很痛苦,而且该写法时间复杂度不是很优秀,由于评测机的关心,我直到出成绩之前都不知道自己过了 )
#include<bits/stdc++.h>
#define ll long long
#define FOR(i,a,b) for(int i=(a),i##i=(b);i<=i##i;++i)
#define ROF(i,a,b) for(int i=(a),i##i=(b);i>=i##i;--i)
using namespace std;
const int N=2e5+10;
typedef vector<int> VT;
void init(int N, std::vector<int> H);
int minimum_jumps(int A, int B, int C, int D);
int n,tzb[N],mx[N<<2],inf;
VT h;
#define ls (rt<<1)
#define rs (rt<<1|1)
void build(int rt,int l,int r){
if(l==r) return mx[rt]=h[l],void();
int mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
mx[rt]=max(mx[ls],mx[rs]);
}
int ask_max(int rt,int l,int r,int L,int R){
if(L>R) return 0;
if(L<=l&&r<=R) return mx[rt];
int mid=(l+r)>>1,as=0;
if(L<=mid) as=max(as,ask_max(ls,l,mid,L,R));
if(R>mid) as=max(as,ask_max(rs,mid+1,r,L,R));
return as;
}
int res;
void dfs_nxt(int rt,int l,int r,int k){
if(mx[rt]<=k) return ;
if(l==r) return res=l,void();
int mid=(l+r)>>1;
if(mx[ls]>k) dfs_nxt(ls,l,mid,k);
else dfs_nxt(rs,mid+1,r,k);
return ;
}
void ask_nxt(int rt,int l,int r,int L,int R,int k){
if(L>R) return ;
if(L<=l&&r<=R) return dfs_nxt(rt,l,r,k);
int mid=(l+r)>>1;
if(L<=mid) ask_nxt(ls,l,mid,L,R,k);
if(R>mid&&res==inf) ask_nxt(rs,mid+1,r,L,R,k);
return ;
}
void dfs_pre(int rt,int l,int r,int k){
if(mx[rt]<=k) return ;
if(l==r) return res=l,void();
int mid=(l+r)>>1;
if(mx[rs]>k) dfs_pre(rs,mid+1,r,k);
else dfs_pre(ls,l,mid,k);
return ;
}
void ask_pre(int rt,int l,int r,int L,int R,int k){
if(L>R) return ;
if(L<=l&&r<=R) return dfs_pre(rt,l,r,k);
int mid=(l+r)>>1;
if(R>mid) ask_pre(rs,mid+1,r,L,R,k);
if(L<=mid&&res==inf) ask_pre(ls,l,mid,L,R,k);
return ;
}
int fa[21][N],nx[21][N],tp2[N],dep[N],dep2[N],root;
VT tr[N],ed[N];
void dfs(int u){
dep[u]=dep[fa[0][u]]+1;
int z=tr[u].size(),v=0;
FOR(i,0,z-1){
v=tr[u][i];
dfs(v);
}
return ;
}
void dfs2(int u){
dep2[u]=dep2[nx[0][u]]+1;
int z=ed[u].size(),v=0;
FOR(i,0,z-1){
v=ed[u][i];
dfs2(v);
}
return ;
}
void init(int nn,VT hh){
n=nn-1,h=hh,inf=n+2;
h.push_back(0),h.push_back(0),h.push_back(0);
FOR(i,0,n) tzb[h[i]]=i,fa[0][i]=-1,nx[0][i]=i;
build(1,0,n);
FOR(i,0,n){
int f1=0,f2=0;
res=inf,ask_pre(1,0,n,0,i-1,h[i]),f1=res;
res=inf,ask_nxt(1,0,n,i+1,n,h[i]),f2=res;
if(h[f1]>h[f2]) fa[0][i]=f1,tr[f1].push_back(i);
else fa[0][i]=f2,tr[f2].push_back(i);
if(f1==inf&&f2==inf) fa[0][i]=i;
if(f2!=inf) nx[0][i]=f2,ed[f2].push_back(i);
}
FOR(i,0,n) if(fa[0][i]==i) root=i;
FOR(i,0,n) if(nx[0][i]==i) dfs2(i);
dfs(root);
FOR(k,1,18) FOR(i,0,n) fa[k][i]=fa[k-1][fa[k-1][i]],nx[k][i]=nx[k-1][nx[k-1][i]];
return ;
}
int ans;
void cal(int u,int C,int w1,int w2){
int v=u;
ROF(k,18,0) if(h[fa[k][v]]<=w1) v=fa[k][v];
if(nx[0][v]<C&&h[fa[0][v]]<=w2) v=fa[0][v];
ans=dep[u]-dep[v];
if(v>=C) return ;
u=v;
ROF(k,18,0) if(nx[k][v]<C) v=nx[k][v];
v=nx[0][v];
ans+=dep2[u]-dep2[v];
return ;
}
int minimum_jumps(int A, int B, int C, int D) {
int mx1=ask_max(1,0,n,B,C-1),mx2=ask_max(1,0,n,C,D);
if(mx1>mx2) return -1;
int l=A,r=B,mid=0,as=inf;
while(l<=r){
mid=(l+r)>>1;
if(ask_max(1,0,n,mid,B)<mx2) r=mid-1,A=mid;
else l=mid+1;
}
int us=ask_max(1,0,n,A,B),nw=tzb[us];
cal(nw,C,mx1,mx2),as=ans;
return as;
}
//7 3
//1 2 6 3 4 5 7
//3 3 6 6
//1 3 5 6
//0 1 2 2
//7 3
//3 2 1 6 4 5 7
//4 4 6 6
//1 3 5 6
//0 1 2 2