HDU 3896 Greast TC 倍增lca,dfn,low,fin的运用

一开始还以为要用双联通求缩点,,写疯了,,后面看了别人的题解,真是服了,,又写了好久,,因为不把森林的情况考虑,想水过,因为倍增时顺序搞反了,等等,,最终迎来艰难的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() ;
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值