CF1696D Permutation Graph 题解
jiangtaizhe001 · · 题解
可能更好的阅读体验
题目传送门
题目大意
给定一个长度为
定义
然后建立一个
求
题目解析
首先根据连边的条件我们发现在最短路径中肯定不会往回走。
并且根据定义,如果
问题就转化为了求与当前点有边相连的编号最大的点。
考虑利用单调栈预处理出每个节点有边第一个比它大/小的点,然后往右跳,并且用线段树 / ST 表求出这一段的最值来判断当前的左端点是否为这个区间的最值即可。
时间复杂度
int n,ln,a[maxn],rx[maxn],ry[maxn],st[maxn],top;
int minx[maxn<<2],maxx[maxn<<2],L,R;
void up(int rt){ minx[rt]=mmin(minx[rt<<1],minx[rt<<1|1]); maxx[rt]=mmax(maxx[rt<<1],maxx[rt<<1|1]); }
void build(int l,int r,int rt){
if(l==r){ minx[rt]=maxx[rt]=a[l]; return; } int mid=(l+r)>>1;
build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); up(rt); return;
}
int findmin(int l,int r,int rt){
if(L<=l&&r<=R) return minx[rt]; int mid=(l+r)>>1,res=INF;
if(mid>=L) res=mmin(res,findmin(l,mid,rt<<1));
if(mid<R) res=mmin(res,findmin(mid+1,r,rt<<1|1));
return res;
}
int findmax(int l,int r,int rt){
if(L<=l&&r<=R) return maxx[rt]; int mid=(l+r)>>1,res=-1;
if(mid>=L) res=mmax(res,findmax(l,mid,rt<<1));
if(mid<R) res=mmax(res,findmax(mid+1,r,rt<<1|1));
return res;
}
void work(){
n=read(); ln=ceil(log2(n)); int i; for(i=1;i<=n;i++) a[i]=read(),rx[i]=ry[i]=0; build(1,n,1);
top=1; st[top]=1; for(i=2;i<=n;i++){ while(a[st[top]]<a[i]&&top) rx[st[top]]=i,top--; st[++top]=i; }
top=1; st[top]=1; for(i=2;i<=n;i++){ while(a[st[top]]>a[i]&&top) ry[st[top]]=i,top--; st[++top]=i; }
int now=1,ri=1,cnt=0;
while(1){
now=ri; if(ri==n) break; ri=now+1; cnt++;
if(a[now]<a[ri]) while(rx[ri]){ L=now; R=rx[ri]; if(findmin(1,n,1)!=a[now]) break; ri=rx[ri]; }
else while(ry[ri]){ L=now; R=ry[ri]; if(findmax(1,n,1)!=a[now]) break; ri=ry[ri]; }
} print(cnt),pc('\n');
}
int main(){
//freopen("1.in","r",stdin);
//freopen(".out","w",stdout)
int T=read(); while(T--) work(); return 0;
}