AGC005_E Sugigma The Showdown

题面翻译
Sigma 和 Sugim在玩一个游戏。
给你两棵树,初始Sigma站在一棵树的编号为x的节点上,Sugim站在另一棵树的编号为y的节点上。Sigma和Sugim轮流行动(第一轮Sigma行动,第二轮Sugim行动),每次行动他们会移动自己所在树的相邻节点上。如果某次行动过后他们站在相同编号的节点上,则游戏结束。Sigma想让游戏尽可能持久,Sugim想让游戏尽快结束。问游戏会在第几轮行动后结束,如果不会结束则输出-1。

思路
如果你知道如何判-1,这题就会迎刃而解。
同时你只要大力猜结论,你也可以手撕此题。
然后我们就想啊想,想到了一个模糊的性质:如果Sigma从某个点到某另一个点的最短距离小于Sugim对应点之间的最短距离,而且这两点在两棵树上的对应路径之间不存在相同的点,那Sigma不是可以来回移动,而Sugim总是抓不到他?哦对,Sigma还得在Sugim到这两点中一点之前先行到达才行。那这么说,这个路径上的每条边貌似也满足性质?
于是,你再仔细想想,就会得到一个结论:设Sigma所在树边集为E1,sugim所在树边集为E2,dis1(a,b)表示a,b在第一棵树上的距离,dis2(a,b)表示a,b在第二棵树上的距离,输出-1的情况当且仅当存在某条边\(\in\)E1,设这条边两端点为u和v,Sigma能在Sugim到达之前到达这两点中一点,且\(|dis2(u,v)|> 2\)
那游戏总会结束怎么办?那我们总得在Sigma能提前到的点中选一个作为游戏结束点吧。对了,那就选一个点u满足dis2(y,u)是最大的吧。那么我们还可以在游戏快结束时再挣扎一下吗(向左右移动),但是既然所有所有在第一棵树上相邻的点在第二棵树上的距离都小于等于
2,那移动也没有什么意义了。
然后你就A掉了此题。
哦对了,如何判断Sigma能否抢先到达这个点也很简单,只需满足Sigma到这个点这一路上每个点都符合dis1(u,x)<dis2(u,y)即可。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=2e5;
int n,x,y,tot;
int pre[maxn*4+8],son[maxn*4+8];
bool can[maxn+8];
queue<int>st;
 
int read()
{
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
 
struct Tree
{
    int now[maxn+8],dep[maxn+8],f[maxn+8][20];
    void add(int u,int v)
    {
	pre[++tot]=now[u];
	now[u]=tot;
	son[tot]=v;
    }
    void build(int x)
    {
	dep[x]=dep[f[x][0]]+1;
	for (int i=1;i<=log(dep[x])/log(2);i++) f[x][i]=f[f[x][i-1]][i-1];
	for (int p=now[x];p;p=pre[p])
	    {
		int child=son[p];
		if (f[x][0]==child) continue;
		f[child][0]=x;
		build(child);
	    }
    }
    int Get_Lca(int x,int y)
    {
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=log(dep[x])/log(2);~i;i--)
	    if (dep[f[x][i]]>=dep[y]) x=f[x][i];
	if (x==y) return x;
	for (int i=log(dep[x])/log(2);~i;i--)
	    if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
    }
    int Get_Dis(int x,int y){return dep[x]+dep[y]-dep[Get_Lca(x,y)]*2;}
}T1,T2;
 
void solve()
{
    //for (int i=1;i<=n;i++) printf("%d ",can[i]);puts("");
    int ans=0;
    for (int i=1;i<=n;i++)
	if (can[i])
	    {
		ans=max(ans,T2.dep[i]-1);
		for (int p=T1.now[i];p;p=pre[p])
		    {
			int child=son[p];
			if (T2.Get_Dis(child,i)>2)
			    {
				puts("-1");
				exit(0);
			    }
		    }
	    }
    printf("%d\n",ans*2);
}
 
void prepare()
{
    can[x]=1;
    st.push(x);
    while(!st.empty())
	{
	    int x=st.front();st.pop();
	    for (int p=T1.now[x];p;p=pre[p])
		{
		    int child=son[p];
		    if (can[child]) continue;
		    if (T1.dep[child]<T2.dep[child]) can[child]=1,st.push(child);
		}
	}
}
 
int main()
{
    n=read(),x=read(),y=read();
    if (x==y)
	{
	    puts("0");
	    return 0;
	}
    for (int i=1;i<n;i++)
	{
	    int u=read(),v=read();
	    T1.add(u,v),T1.add(v,u);
	}
    for (int i=1;i<n;i++)
	{
	    int u=read(),v=read();
	    T2.add(u,v),T2.add(v,u);
	}
    T1.build(x),T2.build(y);
    prepare();
    solve();
    return 0;
}
posted @ 2018-12-26 09:36  Alseo_Roplyer  阅读(300)  评论(0编辑  收藏  举报