【强连通分量】P1407[国家集训队]稳定婚姻 解题报告

本文一样发布于我的Luogu博客,点击https://yuwenzhou-sakana-chn.blog.luogu.org/solution-p1407以获得更好的markdown题解

关于这道题,我首先想说的是:

题面过于鬼畜

具体怎么鬼畜、有多鬼畜、哪里鬼畜详见讨论区(笑到头掉)!

这题的大众做法大家都会(指来指去的),但是从讨论区看出大家好像并不是很能理解……

那就让我们来分析一下这个大众做法吧!

前置芝士:Tarjan求强连通分量 这是日报 这是板子

一、究竟谁指谁

按照题解区的主流,一句话总结就是:

情人男指女,夫妻女指男

可是,真的是这样吗? 我用事实告诉你,不然!(反着指AC实例)

其实这个容易看出,因为根据著名的Kosaraju算法的原理,一个图反向连边的强连通分量和原图是一样的。所以,只要两次连的边方向不一样就行了

而这样指又有什么用呢?让我们来分析一下样例:

二、指来指去的用处

首先鉴于题目的名字实在是没有什么意思,我们仿照样例重新画一张图: 

这里,我们请出了日本著名文学家横滨港口著名侦探和黑手党: 太宰(dazai)治、中原(Nakahara)中也、芥川(Ryunosuke)龙之介、中岛(Nakajima)敦!

你告诉我性别有问题?当然是因为这样你们就不会关心男男女女的了就是没有问题!

在这张图中我们用紫色线表示共事关系,用蓝色线表示曾经有过关系。A组的成员表示主要负责人,B组的成员表示助手。(为什么是共事关系?当然是因为怕你们想多了原题过于鬼畜)

我们把“指向”定义为一种“投靠关系”,就是如果有任务的话这个人会去找谁。

首先我们让所有搭档都闹翻。我们假定关系破裂都是因为主要负责人叛变导致的,那么按照题意他会去找曾经有过关系的非搭档的人,而不知道情况的助手领到任务只会去找他的主要负责人。如下图:

或者下图:

这时关系稳定的标志就是:有一个任务出现的时候,不论是谁领到的,如果不找原先的搭档,就不能找到人了。

那么为什么会形成一个强连通分量呢?

首先我们来看环的情况。把上图的点重新布局一下,容易得到这样一个环形: 

每个人只要沿着环找下去,总能找到一个人共事的。因为将曾经的关系当做等价的线条考虑进去了,所以肯定有人没有找自己现在的搭档。

当图不再是环的时候呢?

我们再引入两个点:森(Mori)欧外和尾崎(Ozaki)红叶(就是莫得图),然后乱加一些关系:

此时我们可以得到这样一个强连通分量:

那这样的强连通分量为什么是不稳定的关系呢?因为强连通分量的点两两可达,所以不存在一个人孤苦无依的情况。

这样,只要沿着“指向”的箭头寻找,每个人一定能找到一个空的伙伴和他搭档。(因为两两互达的缘故,就算是一开始找错人,这个步骤重复一定次数肯定能安排上掉的!)

这时如果双方都在一个强连通分量里面,那么他们一定可以找到NTR换搭档的方法。(因为搭档双方是不相互指的)

这个时候双向边就明显不适应了(会使得“指向”关系不符合我们的预期),所以只能是单向边。

三、口胡实现方法

理解了这个,究竟谁叛变不叛变已经无所谓了,因为明明拆了重组也很好吃啊这个代码已经可以写了。

我们只要给不同的强连通分量染色,然后看看夫妻双方在不在同一个强连通分量里面(因为这样就是戴绿帽子了)就好。

存图可以考虑用map提供的hash对应字符串和标号,当然采用其他的办法对应也可以。这里因为懒就采用大众的map做法了。

AC code(特地安排了反着指的):

#include<iostream>
#include<map>
using namespace std;
#define N 8010
#define M 40010
struct line
{
    int to,next;
}ed[M];
map <string,int> a;//这里
int dfn[N],low[N],head[N],cnt,tot,n,m;
bool ins[N];
int s[N],top,color[N],sum,deep;
void addline(int u,int v)
{
    ed[++cnt].to=v;
    ed[cnt].next=head[u];
    head[u]=cnt;
}
void tarjan(int pos)//板子
{
    dfn[pos]=++deep;
    low[pos]=deep;
    ins[pos]=1;
    s[++top]=pos;
    for(int i=head[pos];~i;i=ed[i].next)
    {
        int y=ed[i].to;
        if(!dfn[y])
        {
            tarjan(y);
            low[pos]=min(low[pos],low[y]);
        }
        else
        {
            if(ins[y])
                low[pos]=min(low[pos],low[y]);
        }
    }
    if(dfn[pos]==low[pos])
    {
        color[pos]=++sum;
        ins[pos]=0;
        while(s[top]!=pos)
        {
            color[s[top]]=sum;
            ins[s[top]]=0;
            top--;
        }
        top--;
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n+n;i++)
        head[i]=-1;
    string s1,s2;
    for(int i=1;i<=n+n;i+=2)//女单男双
    {
        cin>>s1>>s2;
        a[s1]=i;
        a[s2]=i+1;
        addline(i+1,i);
    }
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>s1>>s2;
        addline(a[s1],a[s2]);
    }
    for(int i=1;i<=n+n;i++)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=n+n;i+=2)//分析婚姻
    {
        if(color[i]==color[i+1])//可以戴绿帽
            cout<<"Unsafe"<<endl;
        else
            cout<<"Safe"<<endl;
    }
    return 0;
}

最后,因为本蒟蒻太菜,欢迎摘虫和hack!

原文地址:https://www.cnblogs.com/YuWenzhou-Sakana/p/11794026.html