UVA1327 King's Quest题解

· · 题解

UVA1327 King's Quest

传送门

Blog

啥?你问我为啥没AC?看这里

Waiting了六个月

但是我的代码已经在某OJ AC了,不用担心正确性

题目大意

一个国王有一堆儿子,每个儿子又一堆喜欢的妹纸,有一张保证合法的配对表,现在让你求每个儿子可以和哪几个妹纸结婚。在这个儿子喜欢这个妹纸的前提下,必须保证每个儿子都有至少一个自己的妹纸。

思路

很明显,题目已经给出了每个儿子喜欢的妹纸,所以把儿子和喜欢的妹纸连上一条单向边

所以我们可以想到对着那个奇怪的表把每个妹纸和她对应的儿子连上一条单向边

注意:这里把 AB 连上一条单向边理解为前者连向后者)

接着,我们发现,对于某些儿子和某些妹子,他们组成了若干个强连通分量

在这个强连通分量中所有儿子和妹子的配对都能互换

看到其他题解都没有给出证明,我就写一下我自己相处的证明。如有漏洞,欢迎提出。如果想A题而不考虑证明,可以直接跳到代码部分

两个儿子 uv 之间不可能有直接连线(为啥感觉怪怪的)

所以对于一个结构最简单的强连通分量,如果儿子 u 想连接到儿子 v ,必须将一个妹纸 a 作为桥梁,连出一条 u \rightarrow a \rightarrow v 的路径

同理,如果儿子 v 想连接到儿子 u ,必须将一个妹纸 b 作为桥梁,连出一条 v \rightarrow b \rightarrow u 的路径

此时,孪生出两种情况

  1. a = b

那么这时,如果把儿子列在左边,妹纸列在右边,就形成了一个 > 形结构

  1. a \ne b

此时,形成了一个 8 字型的结构

我们暂且成这两个结构为基础结构

稍加枚举,就可知基础结构中的儿子和妹纸可以互换

而每一个强连通分量可以看做多层基础结构的叠加和拼接

所以每一个强连通分量中儿子和妹纸的配对可以互换

Code

知道如何建图这道题就是板子题啦

#include <bits/stdc++.h>
using namespace std;

vector <int> G[200002];
int n,k;
int tim;
bool instack[200002];
stack <int> s;
int dfn[200002];
int low[200002];
int cnt;
int color[200002];

#define R register

inline int read()
{
    R int x = 0;
    R char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9')
    {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return x;
}

inline void write(int x)
{
    if(x == 0) return;
    write(x / 10);
    putchar(x % 10 + '0');
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++tim;
    s.push(u);
    instack[u] = true;
    for(int i = 0;i < G[u].size();i++) 
    {
        int www = G[u][i];
        if(!dfn[www])
        {
            tarjan(www);
            low[u] = min(low[u],low[www]);
        }
        else if(instack[www])
            low[u] = min(low[u],dfn[www]);
    }
    if(low[u] == dfn[u])
    {   
        ++cnt;
        int v;
        do
        {
            v = s.top();
            s.pop();
            instack[v] = false;
            color[v] = cnt;
        }while(v != u);
    }
}

int main()
{
    n = read();
    for(int i = 1;i <= n;++i)
    {
        int m;
        m = read();
        for(int j = 1;j <= m;++j)
        {
            k = read();
            G[i].push_back(k + n);
        }
    }
    for(int i = 1;i <= n;++i)
    {
        int x;
        x = read();
        G[x + n].push_back(i);
    }
    for(int i = 1;i <= n;++i)
    {
        if(!dfn[i])
            tarjan(i);
    }
//  for(int i=1;i<=2*n;i++){
//      cout<<color[i]<<"  ";
//  }
//  cout<<endl<<endl;
    vector <int> opop;
    for(int i = 1;i <= n;i++)
    {
        int num = 0;
        for(int j = 0;j < G[i].size();++j)
        {
            int v = G[i][j];
            if(v > n and color[v] == color[i])
                opop.push_back(v - n),++num;
        }
        sort(opop.begin(),opop.end());
        write(num);
        putchar(' ');
        for(int j = 0;j < opop.size();++j)
        {
            write(opop[j]);
            putchar(' ');
        }
        opop.clear();
        putchar('\n');
    }
    return 0;
}

附赠一个很容易被 Hack 的数据

输入

4
1 3
3 3 4 1
2 1 2
2 3 4
4 1 3 2 

输出

1 3 
3 1 3 4 
2 1 2 
2 3 4