题目大意:给出一个无向图(不一定连通),然后给出u,v,w三个点,v 和 w要去u,问是否存在两条路径使得v去u和w去u的路径上不存在任何一条公共边(换句话说一条边只能走一次,问u和v能不能都走到w);
可恶啊,想到了正确的图论模型,可惜只差一点,止步在最后的判断,剩一个小时的时候认为判断太复杂了放弃了(白调了一个小时的代码)。
刚看到可能有点懵,但是一想到怎样才会经过一条边?如果有v,w各自在的地方去u那个点只有一条边连通,那必然要走过同一条边的。很容易想到那样的边其实就是桥吧,再验证一下边连通分量内是否一定都能到,发现在同一个边连通分量内答案是可行的,那么我们可以边连通分量缩点(其实边连通分量缩点和强连通缩点是同一个板子,只有一点细微的区别,这里又学到了一个新的板子,之前用的在书上学的有瑕疵。。)。缩完点重新建图其实就是一棵树嘛,然后变成u,v,两点 如何在树上不经过同一条边走到w点。(在这里分析了可能会走同一条边的情况,结果情况太多了。。。。无语啊)其实只要放过来分析可行的方案就行了,不用分析不可行的方案,反而扰乱判断。三点不重合的情况下唯一可行的方案就是u 在 v,w之间(v,w 不能重合),或者三点都重合也是可行的方案。
虽然场上只A了31个人。。但是完全是一道可以做的题。。尽管数据还有很多坑。。
坑点1:(不知道是不是我操作不规范)使用vector 存图会MLE
坑点2: (其实不是坑点。。细心就行) 题目没保证图连通,意味着v,w可能根本就走不到u,这里要用并查集判断一下
在加了并查集和链式向前星存图后,终于A了这道题。。。说起来其实写得很艰难,不过依然是一道可以A的题。。。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
const int maxm = 1e6+10;
const int mx = 19;
int t,n,m,q,x,y;
int vis[maxn],dfn[maxn],belong[maxn],sta[maxn],cnt,res,top,low[maxn];
int grand[maxn][mx + 1],deep[maxn];
struct ss{
int v,nxt;
}edg[maxm],ee[maxm];
int head[maxn],hh[maxn],ct,cct;
int p[maxn];
int find(int x){
return x == p[x] ? x : p[x] = find(p[x]);
}
void init(){
cnt = res = top = 0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(grand,0,sizeof(grand));
memset(head,-1,sizeof(head));
memset(hh,-1,sizeof(hh));
memset(vis,0,sizeof(vis));
memset(deep,0,sizeof(deep));
memset(belong,0,sizeof(belong));
ct = cct = 0;
}
void add(int u,int v){
edg[ct].v = v;
edg[ct].nxt = head[u];
head[u] = ct++;
}
void ad(int u,int v){
ee[cct].v = v;
ee[cct].nxt = hh[u];
hh[u] = cct++;
}
void tarjan(int s,int fa){
low[s] = dfn[s] = ++cnt;
sta[++top] = s;
bool flag = false;
for(int i = head[s]; i + 1; i = edg[i].nxt){
int v = edg[i].v;
if(v == fa && !flag){
flag = true;
continue;
}
if(!dfn[v]) tarjan(v,s);
low[s] = min(low[s],low[v]); //规范操作其实是对遍历到访问过的结点去low,对原本没访问过的点取dfn
//不过边缩是可以直接取low的,点缩怕是不行,会影响判断(想一想)
}
if(low[s] == dfn[s]){
res++;
do{
belong[sta[top]] = res;
vis[sta[top]] = 0;
}while(sta[top--] != s);
}
}
void dfs(int s,int fa){
for(int i = 1; i < mx; i++)
grand[s][i] = grand[grand[s][i - 1]][i - 1];
for(int i = hh[s]; i + 1; i = ee[i].nxt){
int v = ee[i].v;
if(v == fa || deep[v]) continue;
grand[v][0] = s;
deep[v] = deep[s] + 1;
dfs(v,s);
}
}
int lca(int u,int v){
if(deep[u] < deep[v]) swap(u,v);
for(int i = mx; i >= 0; i--){
if(deep[grand[u][i]] >= deep[v]){
u = grand[u][i];
}
}
for(int i = mx; i >= 0; i--){
if(grand[u][i] != grand[v][i]){
u = grand[u][i];
v = grand[v][i];
}
}
if(u == v) return u;
else return grand[u][0];
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&q);
init();
for(int i = 1; i <= n; i++) p[i] = i;
for(int i = 1; i <= m; i++){
scanf("%d%d",&x,&y);
if(x == y) continue;
add(x,y);
add(y,x);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i,-1);
for(int i = 1; i <= n; i++){
for(int j = head[i]; j + 1; j = edg[j].nxt){
int v = edg[j].v;
if(belong[i] != belong[v]){
ad(belong[i],belong[v]);
ad(belong[v],belong[i]);
p[find(belong[i])] = p[find(belong[v])];
}
}
}
for(int i = 1; i <= res; i++){
if(!deep[i]){
deep[i] = 1;
dfs(i,-1);
}
}
for(int i = 1; i <= q; i++){
int w,u,v;
scanf("%d%d%d",&u,&v,&w);
int ui = belong[u];
int vi = belong[v];
int wi = belong[w];
if(find(ui) != find(vi) || find(ui) != find(wi)){
puts("No");
continue;
}
int lv = lca(vi,wi);
int ans1 = lca(ui,vi);
int ans2 = lca(ui,wi);
int ans3 = lca(ui,lv);
if(vi != wi){
if(ans3 == lv && (ans1 == ui || ans2 == ui)) puts("Yes");
else puts("No");
}
else{
if(vi == ui) puts("Yes");
else puts("No");
}
}
}
return 0;
}