BZOJ2791: [Poi2012]Rendezvous

111 篇文章 0 订阅
4 篇文章 0 订阅

题目大意:有n个点,每个点有一条出边,再给出q组询问,每组询问由两个顶点a、b组成,要求输出满足下面条件的x、y:
1. 从顶点a沿着出边走x步和从顶点b沿着出边走y步后到达的顶点相同。
2. 在满足条件1的情况下max(x,y)最小。
3. 在满足条件1和2的情况下min(x,y)最小。
4. 在满足条件1、2和3的情况下x>=y。
如果不存在满足条件1的x、y,输出-1 -1。

2,3,4条件是搞笑的,没有spj,然后就得手动弄出一组定义的解

首先这个图可以看成一堆环,环上每个节点有一个有向的树指向它(大小可能为0)

然后求x,y的话可以分三种情况讨论

1.a,b不在一个联通块上, 则无解

2.a,b在同一个环的同一个节点的子树下,那就是都走到LCA

3.那肯定是两个点都走到环上,然后其中一个向另一个移动,这种情况只需要算出两个解然后根据他给的比较规则来选一个更优的

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500010
using namespace std;
int a[N];
bool vis[N],zai[N],inc[N];
int s[N],t;
int fa[N][21],bel[N],num[N],tree[N],d[N];
int tot[N],cnt;
void dfs(int x)
{
	vis[x]=true;
	t++;s[t]=x;zai[x]=true;
	if(vis[a[x]])
	{
		if(!zai[a[x]]) bel[x]=bel[a[x]],fa[x][0]=a[x],d[x]=d[a[x]]+1,tree[x]=tree[a[x]];
		else
		{
			cnt++;
			while(s[t]!=a[x])
			{
				inc[s[t]]=true;
				t--;
			}
			inc[s[t]]=true;
			tree[x]=x;
			bel[x]=cnt;
			tot[cnt]=1;
			num[x]=1;
		}
		zai[x]=false;
		return;
	}
	dfs(a[x]);
	if(inc[x])
	{
		tree[x]=x;
		bel[x]=cnt;
		tot[cnt]++;
		num[x]=tot[cnt];
	}
	else
	{
		d[x]=d[a[x]]+1;
		tree[x]=tree[a[x]];
		bel[x]=bel[a[x]];
		fa[x][0]=a[x];
	}
	zai[x]=false;
}
int LCA(int x,int y)
{
	int i=20;
	if(d[x]<d[y]) swap(x,y);
	while(d[x]>d[y])
	{
		if(d[x]-d[y]>=(1<<i)) x=fa[x][i];
		i--;
	}
	if(x==y) return x;
	i=20;
	while(fa[x][0]!=fa[y][0])
	{
		if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
		i--;
	}
	return fa[x][0];
}
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	int i,j,x,y,z,ans1,ans2,cur1,cur2;
	for(i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(i=1;i<=n;i++)
	if(!vis[i]) dfs(i);
	for(j=1;j<=20;j++)
	for(i=1;i<=n;i++)
	fa[i][j]=fa[fa[i][j-1]][j-1];
	while(q--)
	{
		scanf("%d%d",&x,&y);
		if(bel[x]!=bel[y]) printf("-1 -1\n");
		else if(tree[x]==tree[y])
		{
			z=LCA(x,y);
			printf("%d %d\n",d[x]-d[z],d[y]-d[z]);
		}
		else
		{
			int tmp1=tree[x],tmp2=tree[y];
			ans1=d[x]+num[tmp1]-num[tmp2]+(num[tmp1]>=num[tmp2]?0:tot[bel[x]]);
			ans2=d[y];
			cur1=d[x];
			cur2=d[y]+num[tmp2]-num[tmp1]+(num[tmp2]>=num[tmp1]?0:tot[bel[x]]);
			if(max(ans1,ans2)!=max(cur1,cur2))
			{
				if(max(ans1,ans2)>max(cur1,cur2))
				ans1=cur1,ans2=cur2;
			}
			else if(min(ans1,ans2)!=min(cur1,cur2))
			{
				if(min(ans1,ans2)>min(cur1,cur2))
				ans1=cur1,ans2=cur2;
			}
			else if(ans1<ans2)
			swap(ans1,ans2);
			printf("%d %d\n",ans1,ans2);
		}
	}
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值