HDU 3896 Greatest TC 雙連通份量

題意

給一個連通的無向圖,有兩種詢問:ide

  • \(a, b, c, d\),問若是刪掉\(c,d\)之間的邊,\(a,b\)之間是否還連通
  • \(a, b, c\),問若是刪掉頂點\(c\),\(a,b\)之間是否還連通

分析

首先DFS一遍求出進入節點的時間戳\(pre(u)\),離開節點的時間戳\(post(u)\)以及當前節點的子樹中能鏈接到的最小的DFS序\(low(u)\)。
而後預處理一下\(u\)的\(2^i\)級祖先,方便計算\(u\)的任意級祖先。post

考慮第一種查詢

不妨設\(c\)是\(d\)的兒子節點,若是\(c,d\)之間是一個橋而且\(a,b\)兩個節點一個在\(c\)的子樹中一個不在,這種狀況下是不連通的。
其餘狀況都是連通的。spa

考慮第二種查詢

分紅三種狀況討論:pwa

  • \(a,b\)都在子樹\(c\)中,若是\(a,b\)在\(c\)的同一個兒子子樹中那麼去掉\(c\)是連通的。
    不然,讓\(a,b\)往上跳,變成\(c\)的兩個兒子。若是\(low(a) \geq pre(c)\)或\(low(b) \geq pre(c)\)有一個成立,那麼是不連通的。code

  • \(a,b\)只有一個在子樹\(c\)中,因爲對稱性,不妨假設\(a\)在子樹\(c\)中。
    一樣讓\(a\)往上跳,變成\(c\)的兒子。若是\(low(a) \geq pre(c)\)那麼不連通,不然連通。string

  • \(a,b\)都不在子樹\(c\)中,那麼去掉\(c\)徹底沒有任何影響,因此仍是連通的。it

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 100000 + 10;
const int maxm = 1000000 + 10;

struct Edge
{
	int v, nxt;
	Edge() {}
	Edge(int v, int nxt): v(v), nxt(nxt) {}
};

int ecnt, head[maxn];
Edge edges[maxm];

void AddEdge(int u, int v) {
	edges[ecnt] = Edge(v, head[u]); head[u] = ecnt++;
	edges[ecnt] = Edge(u, head[v]); head[v] = ecnt++;
}

int n, m;

int fa[maxn], dep[maxn];
int dfs_clock, pre[maxn], post[maxn], low[maxn];
bool isbridge[maxn], iscut[maxn];

void dfs(int u) {
	bool flag = false;
	int child = 0;
	pre[u] = low[u] = ++dfs_clock;
	for(int i = head[u]; ~i; i = edges[i].nxt) {
		int v = edges[i].v;
		if(v == fa[u] && !flag) { flag = true; continue; }
		child++;
		if(!pre[v]) {
			fa[v] = u;
			dep[v] = dep[u] + 1;
			dfs(v);
			low[u] = min(low[u], low[v]);
			if(low[v] >= pre[u]) {
				iscut[u] = true;
				if(low[v] > pre[u]) isbridge[v] = true;
			}
		} else low[u] = min(low[u], pre[v]);
	}
	if(u == 1 && child == 1) iscut[u] = false;
	post[u] = dfs_clock;
}

int anc[maxn][20];

void preprocess() {
	memset(anc, 0, sizeof(anc));
	for(int i = 1; i <= n; i++) anc[i][0] = fa[i];
	for(int j = 1; (1 << j) < n; j++)
		for(int i = 1; i <= n; i++) if(anc[i][j-1])
			anc[i][j] = anc[anc[i][j-1]][j-1];
}

int upward(int u, int x) {
	for(int i = 0; i < 20; i++)
		if((x >> i) & 1) u = anc[u][i];
	return u;
}

int insubtree(int u, int v) {
	if(pre[v] <= pre[u] && pre[u] <= post[v]) return 1;
	return 0;
}

bool juedgeVertex(int a, int b, int c) {
	int in1 = insubtree(a, c);
	int in2 = insubtree(b, c);
	if(in1 & in2) {
		a = upward(a, dep[a] - dep[c] - 1);
		b = upward(b, dep[b] - dep[c] - 1);
		if(a == b) return true;
		if(low[a] >= pre[c]) return false;
		if(low[b] >= pre[c]) return false;
	}
	if(in1 ^ in2) {
		if(!in1) swap(a, b);
		a = upward(a, dep[a] - dep[c] - 1);
		if(low[a] >= pre[c]) return false;
	}
	return true;
}

int main()
{
	while(scanf("%d%d", &n, &m) == 2) {
		ecnt = 0;
		memset(head, -1, sizeof(head));
		while(m--) {
			int u, v;
			scanf("%d%d", &u, &v);
			AddEdge(u, v);
		}

		dfs_clock = 0;
		memset(pre, 0, sizeof(pre));
		memset(isbridge, false, sizeof(isbridge));
		memset(iscut, false, sizeof(iscut));
		dfs(1);
		preprocess();

		int q;
		scanf("%d", &q);
		while(q--) {
			int op, a, b, c, d;
			scanf("%d%d%d%d", &op, &a, &b, &c);
			bool ok = true;
			if(op == 1) {
				scanf("%d", &d);
				if(dep[c] < dep[d]) swap(c, d);
				int in1 = insubtree(a, c);
				int in2 = insubtree(b, c);
				if(isbridge[c] && (in1 ^ in2) == 1) ok = false;
			} else {
				ok = juedgeVertex(a, b, c);
			}
			printf("%s\n", ok ? "yes" : "no");
		}
	}

	return 0;
}
相關文章
相關標籤/搜索