CF1696D Permutation Graph 题解

可能更好的阅读体验

题目传送门

题目大意

给定一个长度为 \(n\) 排列 \(a\)
定义 \(\operatorname{mn}(i,j)=\min\limits_{k=i}^{j}a_k,\operatorname{mx}(i,j)=\max\limits_{k=i}^{j}a_k\)
然后建立一个 \(n\) 个节点图,如果 \(\operatorname{mn}(i,j)=a_i ,\operatorname{mx}(i,j)=a_j\) 或者 \(\operatorname{mn}(i,j)=a_j ,\operatorname{mx}(i,j)=a_i\),那么就在 \(i,j\) 之间连一条无向边。
\(1\) 号点到 \(n\) 号点的最短路。
\(n\le 10^5\)

题目解析

首先根据连边的条件我们发现在最短路径中肯定不会往回走。
并且根据定义,如果 \(u<v<w\) \(u,v\)\(u,w\) 之间有边,那么 \(v,w\) 之间也肯定有边,所以每次尽量往编号大的节点走。
问题就转化为了求与当前点有边相连的编号最大的点。

考虑利用单调栈预处理出每个节点有边第一个比它大/小的点,然后往右跳,并且用线段树 / ST 表求出这一段的最值来判断当前的左端点是否为这个区间的最值即可。
时间复杂度 \(O(n\log n)\)

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;
}
posted @ 2022-06-28 18:59  jiangtaizhe001  阅读(107)  评论(0编辑  收藏  举报