【题意】给了一颗V<=100000的有向的,但是忽略方向的一颗树,每次查询(u,v)是否可以到达?
【解题方法】随便一个点当成根,那么边有两种,一种是向上一种向下。我们可以预处理dp[u]表示u向上或者下的边的条数,有了这个就可以快速算出u向上或者向下的边的条数。当询问(u,v)是否可以到达的时候,只需要判断u->lca(u,v)lca(u,v)->v路径上的上下边是否相同就可以了。
【AC 代码】
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 100010;
int n,q;
struct edge{int v;bool flag;};
vector<edge>G[maxn];
int dep[maxn],dp[maxn],p[18][maxn];
void dfs(int u,int f,int d){
dep[u]=d;p[0][u]=f;
for(int i=0; i<G[u].size(); i++){
edge e=G[u][i];
if(e.v==f) continue;
dp[e.v]=dp[u]+(e.flag?1:-1);
dfs(e.v,u,d+1);
}
}
void build()
{
dp[1]=0;
dfs(1,-1,0);
for(int i=0; i+1<18; i++){
for(int v=1; v<=n; v++){
if(p[i][v]<0) p[i+1][v]=-1;
else p[i+1][v]=p[i][p[i][v]];
}
}
}
int lca(int u,int v){
if(dep[u]>dep[v]) swap(u,v);
for(int i=0; i<18; i++){
if(dep[v]-dep[u]>>i&1) v=p[i][v];
}
if(u==v) return u;
for(int i=17; i>=0; i--){
if(p[i][u]!=p[i][v]){
u=p[i][u],v=p[i][v];
}
}
return p[0][u];
}
int main()
{
int u,v;
while(scanf("%d",&n)!=EOF)
{
for(int i=1; i<=n; i++) G[i].clear();
for(int i=1; i<n; i++){
scanf("%d%d",&u,&v);
G[u].push_back(edge{v,1});
G[v].push_back(edge{u,0});
}
scanf("%d",&q);
build();
while(q--)
{
scanf("%d%d",&u,&v);
int _lca=lca(u,v);
int depu=dep[u]-dep[_lca];
int depv=dep[v]-dep[_lca];
int disu=dp[u]-dp[_lca];
int disv=dp[v]-dp[_lca];
if(depu==-disu&&depv==disv){
printf("Yes\n");
}
else{
printf("No\n");
}
}
}
return 0;
}