若两个路径冲突了,当且仅当这几种情况:
- 终点被对方包含
- 起点被对方包含
- 有一条被完全包含
但是还有一些其他的情况会寄掉,比如一个路径上的所有点都被其他路径覆盖了的时候。
继续思考下去还有很多乱七八糟的东西,但是最终都不了了之。
有一个很神奇的结论是,一定存在一种方案,使得每个点都直接走到终点。
考虑证明:如果存在路径 \(A \rightarrow B\rightarrow C\),两次中间有其他操作,那么这些操作一定不经过 \(B\)。我们将整个树以 \(B\) 为根建图,那么我们可以如下排列操作:
先执行 \(C\) 中的操作,再执行 \(A \rightarrow C\),再执行其他操作。
一直调整下去,我们一定能找到一组满足的操作方案。
那么我们只需要考虑这些边执行的先后顺序即可,如果囚犯 \(x\) 的起点在 \(y\) 的路径上,那么 \(x\) 必须先于 \(y\) 走;如果囚犯 \(x\) 的终点在 \(y\) 的路径上,那么 \(x\) 必须后于 \(y\) 走。我们对应连边,如果该图存在环则一定无解,否则我们可以按照拓扑序一个一个走即可。
连边用线段树优化建图随便搞搞就行了。
可能这种多个人同时移动,都会想一想能不能直接移动到末尾吧。
#include<bits/stdc++.h>
using namespace std;
#define N 120005
#define s first
#define t second
#define ls u<<1
#define rs u<<1|1
#define pii pair<int,int>
int tt,n,m,id;
int siz[N],dis[N],dad[N],SS[N],TT[N],son[N],go[N][20],dfn[N],top[N],tot;
basic_string<int> G[N],T[N<<4];int in[N<<4];
void dfs(int u,int fa){
go[u][0]=fa;for(int i=1;i<=19;i++)go[u][i]=go[go[u][i-1]][i-1];
dad[u]=fa;siz[u]=1;dis[u]=dis[fa]+1;son[u]=0;
for(int v:G[u])if(v!=fa)dfs(v,u),siz[u]+=siz[v],son[u]=siz[son[u]]>siz[v]?son[u]:v;
}
void dfs2(int u,int t){
top[u]=t;dfn[u]=++tot;
if(son[u])dfs2(son[u],t);
for(int v:G[u])if((v!=dad[u])&&(v!=son[u]))dfs2(v,v);
}
struct sgt{
int tr[N<<2];
void build(int u,int l,int r,int op){
tr[u]=++id;
if(l==r)return;
int m=(l+r)>>1;build(ls,l,m,op),build(rs,m+1,r,op);
if(op==0)T[tr[ls]]+=tr[u],T[tr[rs]]+=tr[u],in[tr[u]]++,in[tr[u]]++;//printf("%d -> %d\n",tr[ls],tr[u]),printf("%d -> %d\n",tr[rs],u);
else T[tr[u]]+=tr[ls],T[tr[u]]+=tr[rs],in[tr[ls]]++,in[tr[rs]]++;//printf("%d -> %d\n",tr[u],tr[ls]),printf("%d -> %d\n",tr[u],tr[rs]);
}
void upd(int u,int l,int r,int L,int R,int id,int op){//op=0 连向线段树,op=1 连向点
if(L<=l&&r<=R)return !op?(T[id]+=tr[u],in[tr[u]]++):(T[tr[u]]+=id,in[id]++),void();
int m=(l+r)>>1;
if(m>=L)upd(ls,l,m,L,R,id,op);
if(m<R)upd(rs,m+1,r,L,R,id,op);
}
}A,B;//A 下往上,B 上往下
inline int lca(int u,int v){
if(dis[u]<dis[v])swap(u,v);
for(int i=19;i>=0;i--)if(dis[go[u][i]]>=dis[v])u=go[u][i];
if(u==v)return u;
for(int i=19;i>=0;i--)if(go[u][i]!=go[v][i])u=go[u][i],v=go[v][i];
return go[u][0];
}
int gt(int u,int v){
if(lca(u,v)!=u)return dad[u];
//while(dad[v]!=u)v=dad[v];
for(int i=19;i>=0;i--)if(dis[go[v][i]]>dis[u])v=go[v][i];
return v;
}
void work(){
memset(go,0,sizeof(go));
scanf("%d",&n);
for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),G[u]+=v,G[v]+=u;
dfs(1,0);dfs2(1,1);
scanf("%d",&m);id=m;
A.build(1,1,n,0);B.build(1,1,n,1);
for(int i=1,x,y,u,v;i<=m;i++){//a->b a 先动
scanf("%d%d",&x,&y);++SS[x];++TT[y];
if(x==y){
A.upd(1,1,n,dfn[x],dfn[x],i,0);
B.upd(1,1,n,dfn[y],dfn[y],i,1);
continue;
}
u=gt(x,y),v=y;
A.upd(1,1,n,dfn[x],dfn[x],i,0);
while(top[u]!=top[v]){
if(dis[top[u]]<dis[top[v]])swap(u,v);
A.upd(1,1,n,dfn[top[u]],dfn[u],i,1);
u=dad[top[u]];
}
if(dfn[u]>dfn[v])swap(u,v);
A.upd(1,1,n,dfn[u],dfn[v],i,1);
u=x,v=gt(y,x);
B.upd(1,1,n,dfn[y],dfn[y],i,1);
while(top[u]!=top[v]){
if(dis[top[u]]<dis[top[v]])swap(u,v);
B.upd(1,1,n,dfn[top[u]],dfn[u],i,0);
u=dad[top[u]];
}
if(dfn[u]>dfn[v])swap(u,v);
B.upd(1,1,n,dfn[u],dfn[v],i,0);
}
queue<int> Q;int cnt=0;
for(int i=1;i<=id;i++)if(!in[i])Q.push(i);
while(Q.size()){
int u=Q.front();Q.pop();++cnt;
for(int v:T[u])if(!--in[v])Q.push(v);
}
bool f=true;for(int i=1;i<=n;i++)f&=(SS[i]<=1)&(TT[i]<=1);
puts(cnt==id&&f?"Yes":"No");
tot=0;
for(int i=1;i<=n;i++)G[i].clear(),SS[i]=TT[i]=0;
for(int i=1;i<=id;i++)T[i].clear(),in[i]=0;
}
int main(){
scanf("%d",&tt);
while(tt--)work();
}