这是蒟蒻的第三篇题解。
这道题就是绿与被绿的故事。
在读懂题以后,我们可以归纳出,如果这段姻缘是Unsafe
的,那么这么几个人的关系必须是是个环。
什么意思呢?
以样例2为例:
假设奇数的是 male ,偶数的是 female 。
1 2 3 4分别代表 Melanie Ashley Scarlett Charles.
那么在如图这个环中:原本的夫妻是12和34,但是由于他们在一个环中,说白了,就是在这个强连通分量中,他们可以任意更换对象。(狗血)
样例1不必多说,(三角恋是不会有好结果的),因为这三个人没有构成强连通分量,所以婚姻安全了。(实际上就是把上图的1-4边断掉)
所以就很明显,我们只需要建好图,然后运用 tarjan 求解强连通分量,如果一对夫妻在同一个强连通分量里面,那么婚姻就有危机了,否则就会风平浪静。
然后又有一个问题了:怎么建图。
原题读完后,很容易想到无向图,即建双边。
但是!!!
如果这样的话,我们上面所推导的一切全都前功尽弃了,因为无向图求强连通分量\(==\)想peach.强连通分量是针对有向图来说的。
所以我们在建图时,要把夫妻和情侣都拆开建图。
那怎么拆才能形成环呢?
有三种方案:
- 男 -> 女
- 女 -> 男
- 男 -> 女 和 女 -> 男分开
很显然,第一种和第二种是无论如何也不会出现环的。
还是以样例2为例,如果按第1种方式建图,那么会是这样:
这就很毒瘤。
第2种同理。
再看第三种:建图结果就像最上面我刚开始讲时用的那幅图。
我再放一遍这个图:
我是将夫妻间用 女 -> 男 存图,情侣间用 男 -> 女 存图。(内部含义自行理解 /kk)
这样的话,就能通过女 -> 男 -> 女 -> 男来用算法找到环。
对了,不会\(tarjan\)的出门右转不谢。
这道题实际上只要弄明白怎么建图,就是一道tarjan 模板题 。
讲了这么一大堆,下面是喜闻乐见的代码时间:
#include<bits/stdc++.h>
using namespace std;
int cnt,n,tail,m,h[8005],dfn[8005],low[8005],id,tarn,bl[8005],x[8005],y[8005];
bool vis[8005];
stack<int> s;
map<string,int> to;//map映射,将名字点映射成数字点
struct node {//链式前向星存图
int to,nxt;
} b[25005];
void add(int x,int y) {//加边操作
b[++cnt].to=y;
b[cnt].nxt=h[x];
h[x]=cnt;
}
void tarjan(int u) {//tarjan算法求强联通分量
s.push(u);
vis[u]=1;
dfn[u]=low[u]=++id;
for(int i=h[u]; i; i=b[i].nxt) {
int v=b[i].to;
if(!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
} else {
if(vis[v]) {
low[u]=min(low[u],dfn[v]);
}
}
}
if(dfn[u]==low[u]) {
tarn++;
int v;
do {
v=s.top();
s.pop();
bl[v]=tarn;
vis[v]=0;
} while(u!=v);
}
}
int main() {
cin>>n;
string a,b;
for(int i=1; i<=n; i++) {
cin>>a>>b;
x[i]=to[a]=++tail;//将女方转换成数字
y[i]=to[b]=++tail;//将男方转换成数字
add(to[a],to[b]);//female -> male 存边
}
cin>>m;
for(int i=1; i<=m; i++) {
cin>>a>>b;
add(to[b],to[a]);//male -> female 存边
}
for(int i=1; i<=tail; i++) {
if(!dfn[i]) {
tarjan(i);
}
}
for(int i=1; i<=n; i++) {
if(bl[x[i]]==bl[y[i]]) {//bl 即 belong 表示当前这个点属于哪一个强连通分量
puts("Unsafe");
} else {
puts("Safe");
}
}
return 0;
}