ZOJ - 4097 Rescue the Princess (边双联通缩点无向图+思维lca)

                                                                                   Rescue the Princess

Princess Cjb is caught by Heltion again! Her knights Little Sub and Little Potato are going to Heltion Kingdom to rescue her.

Heltion Kingdom is composed of  islands, numbered from  to . There are bridges in the kingdom, among which the -th bridge connects the -th island and the -th island. The knights can go through each bridge in both directions.

Landing separately on the -th and the -th island, the two knights start their journey heading to the -th island where the princess is imprisoned. However, as the knights are fat and the bridges are unstable, there will be a risk of breaking down the bridge and falling into the water if they go through one or more common bridges during their journey.

Thus, to successfully bring back the princess, two paths \textbf{with no common bridges} are needed: one starts from the -th island and leads to the -th island, while the other starts from the -th island and also leads to the -th island.

As the princess is caught very often, the knights will ask you for help  times. Each time, given their starting islands and their goal, you need to tell them whether it's possible to find two paths satisfying the constraints above.

Input

There are multiple test cases. The first line of the input contains an integer , indicating the number of test cases. For each test case:

The first line contains three integers ,  and  (, , ), indicating the number of islands, the number of bridges and the number of queries.

The following  lines describe the bridges. The -th line contains two integers and  (), indicating the two islands the -th bridge connects. Notice that different bridges may connect the same pair of islands and a bridge may connect an island to itself.

The following  lines describe the queries. The -th line contains three integers ,  and  (), indicating the island where the princess is imprisoned and the starting islands of the two knights.

It's guaranteed that the sum of  of all test cases will not exceed , the sum of  of all test cases will not exceed , and the sum of  of all test cases will not exceed .

Output

For each test case output  lines indicating the answers of the queries. For each query, if two paths meeting the constraints can be found, output "Yes" (without quotes), otherwise output "No" (without quotes).

Sample Input

2
6 7 4
1 2
2 3
3 1
4 5
5 6
6 4
1 4
4 1 3
1 4 2
1 2 3
1 3 3
2 1 2
1 2
1 1 1
2 1 2

Sample Output

No
Yes
Yes
Yes
Yes
Yes

Hint

For the first sample test case:

  • For the 2nd query, we can select the paths 4-1 and 2-1.
  • For the 3rd query, we can select the paths 2-1 and 3-1.
  • For the 4th query, we can select the paths 3-1 and 3-2-1.

 

For the second sample test case:

  • For the 1st query, as the knights and the princess are on the same island initially, the answer is "Yes".
  • For the 2nd query, as one of the knights are on the same island with the princess initially, he does not need to cross any bridge. The other knight can go from island 1 to island 2 directly.

 

            缩点之后进行思维+lca。具体就是两种情况的lca考虑(其中灰色代表路经点,红色为目的地,白色为骑士起点)

 

#include<cstdio>
#include<cstring>
#include<queue>
#include<iostream>
using namespace std;
const int maxn = 100010;
vector<int>g[maxn], G[maxn];
int tree_num;
int low[maxn], dfn[maxn], Stack[maxn], Belong[maxn];
int inde, top; int Belong_tree[maxn];
int block;//边双连通块数.
bool instack[maxn], vis[maxn];
void Tarjan(int u, int pre, int b) {
	int v;
	low[u] = dfn[u] = ++inde;
	Stack[top++] = u;
	instack[u] = 1;
	Belong_tree[u] = b;
	int pre_cnt = 0;
	for (int v : g[u]) {
		if (v == pre&&pre_cnt == 0) { pre_cnt++; continue; }
		if (!dfn[v]) {
			Tarjan(v, u, b);
			if (low[u] > low[v])low[u] = low[v];
		}
		else if (instack[v] && low[u] > dfn[v])
			low[u] = dfn[v];
	}
	if (low[u] == dfn[u]) {
		block++;
		do {
			v = Stack[--top];
			instack[v] = 0;
			Belong[v] = block;
		} while (v != u);
	}
	return;
}
const int DEG = 18;
int fa[maxn][DEG]; int dep[maxn];
void bfs(int root) {
	queue<int>q;
	dep[root] = 0;
	fa[root][0] = root;
	q.push(root);
	while (!q.empty()) {
		int u = q.front();
		q.pop(); vis[u] = 1;
		for (int i = 1; i < DEG; i++)
			fa[u][i] = fa[fa[u][i - 1]][i - 1];//
		for (int v : G[u]) {
			if (v == fa[u][0])continue;
			dep[v] = dep[u] + 1;
			fa[v][0] = u;
			q.push(v);
		}
	}
}
int lca(int u, int v) {
	if (dep[u] > dep[v])swap(u, v);
	int hu = dep[u], hv = dep[v];
	int tu = u, tv = v;
	for (int det = hv - hu, i = 0; det; det >>= 1, i++) {
		if (det & 1)
			tv = fa[tv][i];
	}
	if (tu == tv)return tu;
	for (int i = DEG - 1; i >= 0; i--) {
		if (fa[tu][i] == fa[tv][i])continue;
		tu = fa[tu][i];
		tv = fa[tv][i];
	}
	return fa[tu][0];
}
void solve(int n) {
	for (int i = 0; i <= n; i++) {
		dfn[i] = 0;
	}
	inde = top = block = 0;
	for (int i = 1; i <= n; i++) {
		if (!dfn[i])
			Tarjan(i, 0, ++tree_num);
	}
}
int main() {
	int n, m, q;
	int te;
	scanf("%d", &te);
	while (te--) {
		scanf("%d%d%d", &n, &m, &q);
		for (int i = 0; i <= n; i++)g[i].clear(), G[i].clear(), vis[i] = 0;
		tree_num = 0;
		for (int i = 0; i < m; i++) {
			int a, b;
			scanf("%d%d", &a, &b);
			g[a].push_back(b); g[b].push_back(a);
		}
		solve(n);
		for (int i = 1; i <= n; i++) {
			for (int v : g[i]) {
				if (Belong[i] != Belong[v]) {
					G[Belong[i]].push_back(Belong[v]);//
				}
			}
		}
		for (int i = 1; i <= block; i++) {
			if (!vis[i])
				bfs(i);
		}
		while (q--)
		{
			int a, b, c;
			scanf("%d%d%d", &a, &b, &c);
			if (Belong_tree[b] != Belong_tree[a] || Belong_tree[b] != Belong_tree[c]) {
				printf("No\n"); continue;
			}
			b = Belong[b]; a = Belong[a]; c = Belong[c];
			int d = lca(a, b), e = lca(a, c), f = lca(b, c);
		//	cout << a << " " << b << " " << c << endl;
		//	cout << d << " " << e << " " << f << endl;
		//	cout << dep[a] << " " << dep[f] << endl;
			if ((e == a && d == a&&f == a) || (e == a&& f == d&&dep[f] < dep[a]) || (d == a&&f == e&&dep[f] < dep[a])) {
				printf("Yes\n");
			}
			else
				printf("No\n");
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值