一开始还以为要用双联通求缩点,,写疯了,,后面看了别人的题解,真是服了,,又写了好久,,因为不把森林的情况考虑,想水过,因为倍增时顺序搞反了,等等,,最终迎来艰难的AC,深深地明白自己还太弱了,如果是比赛怎么搞!!
思路:对于第一种情况,选取c,d中深度深的(假设是c);1,如果a和b都是以c为根的点 2,如果a和b都不是以c为根的点 3,如果c ,d边不是割边 ;yes!!
对于第二种情况;1,如果a和b都是以c根的点,且a和b所在的分支均不是c的点双联通分量 2,如果a和b中只有一个点是以c为根的点,且该点不是以c的双联通分量 3,如
果a和b多不是以c为根的点;yes!!
如何判断一个点是不是以另一个点为根的点???引进fin,代表该dfs离开该点的时间,,则有if(dfn[a] >= dfn[b] && fin[a] <= fiin[b]) a是以b为根的点,,,
#include <iostream>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std ;
const int N = 100000 + 11 ;
const int M = 500000 + 11 ;
struct Greaph {
struct Edge {
int e ;
int next ;
};
Edge err[M<<1] ; int head[N] ; int idx ;
int dfn[N] ; int low[N] ; int fin[N] ; int vist ; int fint ;
int deapth[N] ; int anc[N][25] ; int maxh ;
int n , m ;
void init() {
memset(head , -1 , sizeof(head)) ;
memset(dfn , 0 , sizeof(dfn)) ;
memset(anc , 0 , sizeof(anc)) ;
idx = 1 , vist = 0 , fint = 0 , maxh = 1 ;
}
void add_edge(int a ,int b) {
err[idx].e = b ;
err[idx].next = head[a] ;
head[a] = idx++ ;
}
void addinfo() {
int a , b ;
while(m--) {
scanf("%d%d" ,&a ,&b) ;
add_edge(a , b) ;
add_edge(b , a) ;
}
}
void dfs(int u , int fa) {
dfn[u] = low[u] = ++vist ;
for(int i = head[u] ; i != -1 ; i = err[i].next) {
int e = err[i].e ;
if(e == fa) continue ;
if(dfn[e] == 0) {
deapth[e] = deapth[u] + 1 ;
anc[e][0] = u ;
dfs(e , u) ;
low[u] = min(low[e] , low[u]) ;
}else {
low[u] = min(low[u] , dfn[e]) ;
}
}
fin[u] = ++fint ;
if(deapth[u] > maxh) maxh = deapth[u] ;
}
bool isson(int a ,int c) {
if(dfn[a] >= dfn[c] && fin[a] <= fin[c]) return true ;
return false ;
}
void swim(int& a , int h) {
assert(h >= 0) ;
int k = 0 ;
while(h) {
if(h&1) a = anc[a][k] ;
++k ;
h >>= 1 ;
}
}
int find(int a , int b) {
if(deapth[a] < deapth[b]) {int t = a ;a = b ;b = t;}
swim(a , deapth[a] - deapth[b]) ;
if(a == b) return a ;
int i ;
while(true) {
for(i = 0 ; anc[a][i] != anc[b][i] ; ++i) ;
if(i == 0) {
return anc[a][0] ;
}
if(a == 0 && b == 0) return 0 ;
a = anc[a][i-1] ;
b = anc[b][i-1] ;
}
}
void std_fun() {
init() ;
addinfo() ;
for(int i = 1 ; i <= n ; ++i) {
if(dfn[i] == 0) {
deapth[i] = 1 ;
anc[i][0] = 0 ;
dfs(i , 0) ;
}
}
for(int i = 1 ; (1<<i) <= maxh ;++i){//顺序千万不能搞反 了,,,,调了好久
for(int j = 1 ; j <= n ; ++j){
anc[j][i] = anc[anc[j][i-1]][i-1] ;
}
}
scanf("%d" ,&m) ;
int op , a , b , c , d ;
while(m--) {
scanf("%d" ,&op) ;
bool h = false ;
if(op == 1) {
scanf("%d%d%d%d" ,&a ,&b ,&c ,&d) ;
int ancestor = find(a , b) ;
if(ancestor == 0) {
printf("no\n") ;
continue ;
}
if(deapth[c] < deapth[d]) {int t = c ; c = d ;d = t ;}
bool h1 = isson(a , c) ;
bool h2 = isson(b , c) ;
if(h1 && h2) h= true ;
else if((!h1) && (!h2)) h = true ;
else if(low[c] <= dfn[d]) h = true ;
}else {
scanf("%d%d%d" ,&a ,&b ,&c) ;
int ancestor = find(a , b) ;
if(ancestor == 0|| a == c || b == c) {
printf("no\n") ;
continue ;
}
bool h1 = isson(a , c) ;
bool h2 = isson(b , c) ;
if((!h1) &&(!h2)) h = true ;
else if(h1 && (!h2)) {
swim(a , deapth[a] - deapth[c] - 1) ;
if(low[a] < dfn[c]) h = true ;
}else if((!h1)&& h2) {
swim(b , deapth[b] - deapth[c] - 1) ;
if(low[b] < dfn[c]) h = true ;
}else {
swim(a ,deapth[a] - deapth[c] - 1) ;
swim(b ,deapth[b] - deapth[c] - 1) ;
if(a == b) h = true ;
else if(low[a] < dfn[c] && low[b] < dfn[c]) h = true ;
}
}
printf(h ? "yes\n":"no\n") ;
}
}
}g ;
int main() {
while(scanf("%d%d" ,&g.n ,&g.m)==2) {
g.std_fun() ;
}
}