ioi2018集训队自选题:最短路练习题

题意:链接

 

定义pos[i]表示i这个值在数组里的下标.

我们先用单调栈找到每个元素左边和右边第一个比它大的元素$l_i$和$r_i$,然后建一棵二叉树,我们就叫做maxtree吧 (upd:mdzz 这就是笛卡尔树)

每个点$i$的父亲是$pos[max(l_i,r_i)]$,如果是$r_i$比较大,那么$i$就是左儿子,$l_i$比较大同理。

当$i$是左儿子时,容易证出$pos[l_i]$在树上的位置就是$i$不停向上走时第一次向左上走遇到的点,反之亦然,设$gfa[x]$表示$x$连向的点中非$fa[x]$的点。

对于每个询问,我们先求出$x,y$的$lca t$,如果$t$不为$x$和$y$,那么最短路上要么经过$t$,要么经过$fa[t]$,这个也很好证(大概就是如果$t$是左儿子,那么$t$的左子树中的点不可能向$fa[t]$上边连边,右边同理)。

定义函数$dis(x,y)$表示$x$到$y$的最短路,其中$y$是$x$的祖先。

当$dep[g[fa[x]]]>=dep[y]$时,显然走$gfa$比较优,然后再在最后一步的时候两种方案取个$min$就行了,问题解决。

 

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m;
int a[N];
int st[N],ll[N],rr[N],top;
int ch[N][2];
int fa[N][22],fs[N][22],dep[N];
void dfs(int x,int pr,int op)
{
	if(ch[x][0])
	{
		fs[ch[x][0]][0]=x;
		dep[ch[x][0]]=dep[x]+1;
		if(op==0)
		{
			fa[ch[x][0]][0]=pr;
			dfs(ch[x][0],pr,op);
		}
		else dfs(ch[x][0],x,op);
	}
	if(ch[x][1])
	{
		fs[ch[x][1]][0]=x;
		dep[ch[x][1]]=dep[x]+1;
		if(op==1)
		{
			fa[ch[x][1]][0]=pr;
			dfs(ch[x][1],pr,op);
		}
		else dfs(ch[x][1],x,op);
	}
	return ;
}
void yu()
{
	for(int i=1;i<=17;i++)
	{
		for(int j=1;j<=n;j++)
		{
			fa[j][i]=fa[fa[j][i-1]][i-1];
			fs[j][i]=fs[fs[j][i-1]][i-1];
		}
	}
}
int lca(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	for(int i=17;i>=0;i--)if(dep[fs[x][i]]>=dep[y])x=fs[x][i];
	if(x==y)return x;
	for(int i=17;i>=0;i--)
	{
		if(fs[x][i]!=fs[y][i])
		{
			x=fs[x][i];y=fs[y][i];
		}
	}
	return fs[x][0];
}
int dis(int x,int y)
{
	int ans=0;
	for(int i=17;i>=0;i--)
	{
		if(dep[fa[x][i]]>=dep[y])
		{
			ans+=(1<<i);
			x=fa[x][i];
		}
	}
	if(!fa[x][0])return ans+dep[x]-dep[y];
	return ans+min(dep[x]-dep[y],2);
}
int solve(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	int t=lca(x,y);
	if(t==y)return dis(x,y);
	int tmp=dis(x,t)+dis(y,t);
	if(t!=1)tmp=min(tmp,dis(x,fs[t][0])+dis(y,fs[t][0]));
	return tmp;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		while(top&&a[st[top]]<a[i])top--;
		if(top)ll[i]=st[top];
		st[++top]=i;
	}
	top=0;
	for(int i=n;i>=1;i--)
	{
		while(top&&a[st[top]]<a[i])top--;
		if(top)rr[i]=st[top];
		st[++top]=i;
	}
	ch[1][1]=n;
	for(int i=2;i<=n-1;i++)
	{
		if(a[ll[i]]<a[rr[i]])ch[ll[i]][1]=i;
		else ch[rr[i]][0]=i;
	}
	dep[1]=1;dfs(1,0,0);dfs(1,0,1);
	yu();
	int t1,t2;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&t1,&t2);
		printf("%d\n",solve(t1,t2));
	}
	return 0;
}

  

posted @ 2017-12-19 21:37  SD_le  阅读(1026)  评论(1编辑  收藏  举报
重置按钮