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() ;
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章