題意:
n個點,n-1條紅邊,n-1條藍邊。紅邊構成一棵樹,藍邊構成一棵樹。開始時,A在點x,B在點y。兩人輪流操作,A先手。A可以不動或沿紅邊走,B可以不動或沿藍邊走。當A、B走到同一個點時遊戲結束。A想玩的儘量久,B想盡量快。問會進行多少輪,無限輸出-1。
n<=200000
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<vector>
#define N 210000
#define pb push_back
using namespace std;
struct node{int y,nex;}a[2*N];
int e[N][2],len,fir[N],n,sa,sb,dep[N],dfn[N],las[N],id,f[N],p[N],tail,d[N],ans;
bool b[N],v[N];
vector<int> A[N];
void ins(int x,int y)
{
a[++len].y=y;a[len].nex=fir[x];fir[x]=len;
}
void dfs(int x,int fa)
{
f[x]=fa;
dfn[x]=++id;
dep[x]=dep[fa]+1;
for(int k=fir[x];k;k=a[k].nex)
{
int y=a[k].y;
if(y==fa) continue;
dfs(y,x);
}
las[x]=id;
}
bool check(int x,int y)
{
if(dep[x]>dep[y]) swap(x,y);
if(dfn[x]<=dfn[y] && las[x]>=dfn[y])
{
if(dep[y]-dep[x]>2) return 1;
return 0;
}
if(f[x]==f[y]) return 0;
return 1;
}
void bfs()
{
v[sa]=1;p[1]=sa;tail=1;
for(int i=1;i<=tail;i++)
{
int x=p[i],siz=A[x].size();
for(int k=0;k<siz;k++)
{
int y=A[x][k];
d[y]=d[x]+1;
if(d[y]<dep[y] && v[y]==0) {v[y]=1;p[++tail]=y;}
}
}
}
int main()
{
scanf("%d%d%d",&n,&sa,&sb);
for(int i=1;i<n;i++) scanf("%d%d",&e[i][0],&e[i][1]);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(sb,0);
for(int i=1;i<=n;i++) dep[i]--;
for(int i=1;i<n;i++)
{
int x=e[i][0],y=e[i][1];
if(check(x,y))
b[x]=b[y]=1;
else A[e[i][0]].pb(e[i][1]),A[e[i][1]].pb(e[i][0]);
}
bfs();
for(int i=1;i<=n;i++)
{
if(v[i] && b[i]) {printf("-1\n");return 0;}
if(v[i]) ans=max(ans,dep[i]*2);
}
printf("%d\n",ans);
return 0;
}
題解:
先把以y爲根把B的樹建出來。對於A的一條邊(x,y),x和y在B的樹上距離>2,如果A走到x或y之前或下一輪B都沒能抓到他,那答案就是-1。
把這些邊刪掉後就剩下一些1或2的邊,一個重要的結論就是A用這些邊走不出B的子樹。所以可以bfs一遍,找出A在走到那些點後還不會被B抓到。
如果不能走到-1的點,那麼A就會留在能走到的最深的點,然後放棄治療>_<
於是就可以算答案了。。