P1407 [国家集训队]稳定婚姻

题目描述:

现代生活给人们施加的压力越来越大,离婚率的不断升高已成为现代社会的一大问题。而其中有许许多多的个案是由婚姻中的“不安定因素”引起的。妻子与丈夫吵架后,心如绞痛,于是寻求前男友的安慰,进而夫妻矛盾激化,最终以离婚收场,类似上述的案例数不胜数。

我们已知n对夫妻的婚姻状况,称第i对夫妻的男方为Bi,女方为Gi。若某男Bi与某女Gj曾经交往过(无论是大学,高中,亦或是幼儿园阶段,i≠j),则当某方与其配偶(即Bi与Gi或Bj与Gj)感情出现问题时,他们有私奔的可能性。不妨设Bi和其配偶Gi感情不和,于是Bi和Gj旧情复燃,进而Bj因被戴绿帽而感到不爽,联系上了他的初恋情人Gk……一串串的离婚事件像多米诺骨牌一般接踵而至。若在Bi和Gi离婚的前提下,这2n个人最终依然能够结合成n对情侣,那么我们称婚姻i为不安全的,否则婚姻i就是安全的。

给定所需信息,你的任务是判断每对婚姻是否安全。

输入格式

第一行为一个正整数n,表示夫妻的对数;

以下n行,每行包含两个字符串,表示这n对夫妻的姓名(先女后男),由一个空格隔开;

第n+2行包含一个正整数m,表示曾经相互喜欢过的情侣对数;

以下m行,每行包含两个字符串,表示这m对相互喜欢过的情侣姓名(先女后男),由一个空格隔开。

输出格式

输出文件共包含n行,第i行为“Safe”(如果婚姻i是安全的)或“Unsafe”(如果婚姻i是不安全的)。


题解:
首先输入夫妻的时候,用左边的女性连向右边的男性建边,当然不能用字符串,我们得先map存一下,然后在输入情人关系时,将右边的男性和左边的女性建边,容易发现,如果图中有环一定包含了多对夫妻,那么他们就是不安全的,也就是缩点以后在同一联通块中


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
const int MAXN = 1e5+50;
const int MAXM = 2e5+50;
const int MOD = 1e9+7;
const double PI = acos(-1);
inline char getc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    int x=0,f=1; char ch=getc();
    while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getc(); }
    while(isdigit(ch)) x=x*10+ch-48,ch=getc();
    return x*f;
}
int n,m,tot,cnt,k,top,head[MAXN],to[MAXM],nxt[MAXM];
int dfn[MAXN],low[MAXN],instack[MAXN],st[MAXN],bel[MAXN];
inline void add(int u,int v){
    to[++tot]=v; nxt[tot]=head[u]; head[u]=tot;
}
void tarjan(int u){
    dfn[u]=low[u]=++cnt; st[++top]=u; instack[u]=1;
    for(int i=head[u];i;i=nxt[i]){
        if(!dfn[to[i]]) tarjan(to[i]),low[u]=min(low[u],low[to[i]]);
        else if(instack[to[i]]) low[u]=min(low[u],dfn[to[i]]);
    }
    if(dfn[u]==low[u]){
        ++k; int v;
        do{
            v=st[top--]; instack[v]=0; bel[v]=k;
        }while(u!=v);
    }
}
map<string,int> mp;
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        string a,b; cin>>a>>b;
        mp[a]=i; mp[b]=i+n; add(i,i+n);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        string a,b; cin>>a>>b; add(mp[b],mp[a]);
    }
    for(int i=1;i<=2*n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++){
        if(bel[i]==bel[i+n]) puts("Unsafe");
        else puts("Safe");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值