zoukankan      html  css  js  c++  java
  • 【强连通分量】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!

  • 相关阅读:
    手机维修费杂乱 消协命令设立扶植行业标准
    ImageList控件的使用方法
    三峡实习记之一
    [原创]2天的搜索经验对IIS的问题的全面解决方案
    亲历虚拟机安装Ubuntu Linux系统
    新的一年,新的开始
    VS2003中实现身份验证的探索经验
    [原创]VS2003中ASP.NET实现自定义用户导航控件
    全面控制Windows任务栏
    单刀赴广州
  • 原文地址:https://www.cnblogs.com/YuWenzhou-Sakana/p/11794026.html
Copyright © 2011-2022 走看看