题意简述
给定
2
n
2n
2n个人,一些情侣关系,和
n
n
n对夫妻关系。定义一对"不安全的"夫妻为:即使离婚,但是算上旧情复燃(就是那些情侣)的关系,依然珂以让每个人都有伴侣(也就是还能找到
n
n
n对伴侣)。输出
n
n
n行,按照给定夫妻的顺序,输出每对夫妻关系是否安全。安全输出Safe
,不安全输出Unsafe
。
数据
输入
n//夫妻对数
girl boy
girl boy
...
girl boy
//n行,每行描述一对夫妻关系。保证没有开后宫的,也没有单身的。
//是字符串形式哦!!!!!
m//情侣对数(导致婚姻不安全的罪魁祸首)
girl boy
girl boy
...
girl boy
//m行,每行描述一对情侣关系,不保证没有开后宫的(情侣嘛,一个人有很多是正常的)。
//当然,也珂能有人没有情人
//同样也是字符串形式,但是保证是上面出现过的。
输出
ans
ans
...
ans
//n行
//ans="Safe"或"Unsafe",注意大小写
//对于每对夫妻,输出答案
样例
输入
2
Melanie Ashley
Scarlett Charles
1
Scarlett Ashley
输出
Safe
Safe
输入
2
Melanie Ashley
Scarlett Charles
2
Scarlett Ashley
Melanie Charles
输出
Unsafe
Unsafe
思路
题面很有意♂思
(很明显要建图吧。。。情侣或夫妻都连一条无向边。这很容易想到)
建完图后,想想:什么时候会出现不稳定的婚姻呢?
不难想到,就是一对婚姻关系在偶环上的时候。
珂是我们如何找环呢?
此时我们就要有梦想。我们会
T
a
r
j
a
n
Tarjan
Tarjan求强连通分量,珂是我们不会在无向图上找环。所以此时就要用到建图里面一个(比较)常用的思路:给无向图定向。
现在假设有
4
4
4个人,关系长这样:
(红色女的,蓝色男的,自古红蓝出cp嘛)
(
黑
色
黑色
黑色是夫妻边,
绿
色
\color{#00ff00} {绿色}
绿色是情侣边)
显然这样是不安全的,两对夫妻都不安全。
然后我们要让这四个人组成一个强连通分量。不用说,肯定要这样配:
或者这样:
总之有一点,那就是:
绿色边的方向 和 蓝色边的方向 是相反的!
比如,
绿色是
g
i
r
l
girl
girl到
b
o
y
boy
boy,黑色就得(
d
e
ˇ
i
děi
deˇi)是
b
o
y
boy
boy到
g
i
r
l
girl
girl。
绿色是
b
o
y
boy
boy到
g
i
r
l
girl
girl,黑色就得是
g
i
r
l
girl
girl到
b
o
y
boy
boy。
当然,我也忘了我代码里是怎么样的了,反正只要是反的就珂以了。
这样建完图,跑一遍强连通分量就好了。最后就是判一下一对夫妻边连接的两个点是否在同一个强连通分量里面即珂。如果是,输出Unsafe
,否则输出Safe
。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 100100
class Graph
{
public:
int *head;
int EdgeCount;
struct Edge
{
int To,Label,Next;
}*Ed;
int MAX;
void SET(int len)
{
MAX=(len<<1)+100;
Ed=new Edge[MAX];
head=new int[MAX];
memset(head,-1,sizeof(int)*MAX);
memset(Ed,-1,sizeof(Edge)*MAX);
EdgeCount=0;
}
void clear()
{
memset(head,-1,sizeof(int)*MAX);
memset(Ed,-1,sizeof(Edge)*MAX);
EdgeCount=0;
}
void AddEdge(int u,int v,int w)
{
++EdgeCount;
Ed[EdgeCount]=(Edge){v,w,head[u]};
head[u]=EdgeCount;
}
void Add2(int u,int v,int w)
{
AddEdge(u,v,w);AddEdge(v,u,w);
}
int Start(int u)
{
return head[u];
}
int To(int u)
{
return Ed[u].To;
}
int Label(int u)
{
return Ed[u].Label;
}
int Next(int u)
{
return Ed[u].Next;
}
}G;
map<string,int> bid,gid;//处理一下字符串,由于我懒,我就直接用了map
char b[N][10],g[N][10];
int n,m;
void Input()
{
scanf("%d",&n);
G.SET(100000);
for(int i=1;i<=n;++i)//boy to girl
{
scanf("%s%s",g[i],b[i]);
bid[b[i]]=i;
gid[g[i]]=i;
G.AddEdge(i,i+n,1);
}
scanf("%d",&m);
for(int i=1;i<=m;++i)//girl to boy
{
char t1[10],t2[10];
scanf("%s%s",t2,t1);
int u=bid[t1],v=gid[t2];
G.AddEdge(v+n,u,1);
}
}
//normal
int DFSid[N],low[N],time=0;
bool In[N];stack<int>STK;
int SCCid[N],SCCcnt=0;
//addition
int size[N];
void Tarjan(int u)
{
DFSid[u]=low[u]=++time;
STK.push(u);In[u]=1;
for(int i=G.Start(u);~i;i=G.Next(i))
{
int v=G.To(i);
if (!DFSid[v])
{
Tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (In[v]) low[u]=min(low[u],low[v]);
}
if (DFSid[u]==low[u])
{
int top;
++SCCcnt;
do
{
top=STK.top();STK.pop();
In[top]=0;
SCCid[top]=SCCcnt;
++size[SCCcnt];
} while (top!=u);
}
}【模板】强连通分量
void Soviet()
{
for(int i=1;i<=(n<<1);++i)
{
if (!DFSid[i])
{
Tarjan(i);
}
}
for(int i=1;i<=n;++i)
{
int u=bid[b[i]];
int v=gid[g[i]]+n;
if (SCCid[u]==SCCid[v])//在一个SCC里面
{
puts("Unsafe");//那就是不安全的
}
else
{
puts("Safe");//否则就是安全的
}
}
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
Input();
Soviet();
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}