Atcoder-ABC317G-Rearranging

发布时间 2023-12-08 07:53:33作者: Imcaigou

ABC317G - Rearranging

题意

给出一个 \(n\)\(m\) 列的矩阵,可以将每一行的 \(m\) 个数重新排列,问能不能得到 \(m\) 列都是 \(n\) 的排列的矩阵,能得到则输出任意一个方案。

给出的矩阵满足对于 \(i \in [1,n]\) ,都恰好出现了 \(m\) 次。

思路

前置知识:霍尔定理(Hall's Threorem),二分图完美匹配定义。

首先,没有无解的情况(亲测将 "NO" 改为 "This_is_FFT" 也能过)。

考虑如何证明这件事。

对于任意 \(n\)\(1\) 列的矩阵,可以得到一列 \(n\) 的排列,有解。

假设任意 \(n\)\(m-1\) 列的矩阵有解。

考虑任意一个 \(n\)\(m\) 列的矩阵。

考虑对于第 \(i\) 行的所有数 \(a_{i,j}\) 建边 \(i \to a_{i,j}\)。这时整张图构成了二分图,左边是行,点集记为 \(V\),右边是行内具体的数,图中共有 \(nm\) 条边。同时记点 \(i\) 指向的点构成的集合为 \(T_i\)。对于这张图,每个点的度数都为 \(m\)

霍尔定理说明:若二分图满足 \(\forall V' \subseteq V : |\bigcup_{i\in V'} T_i| \ge |V'|\),则原二分图一定存在完美匹配。

而对于原图中选出 \(V' \subseteq V\),记 \(k=|V'|\),则 \(\sum_{i \in V'}|T_i| = mk\),由于对于这些 \(T_i\) 中的任意数最多会有 \(m\) 个相同的数,也就是说互不相同的点的个数一定大于等于 \(k\),即 \(V' \subseteq V : |\bigcup_{i\in V'} T_i| \ge |V'|\),而因为 \(V'\) 可以任取,所以原图满足霍尔定理,故原图有一组完美匹配。

这组完美匹配可以得到一列 \(n\) 的排列,因为左部和右部在完美匹配中行和数一一对应。然后我们将这一列删去,同时在图中删去这组完美匹配,原矩阵变为一个 \(n(m-1)\) 的矩阵。因为 \(n(m-1)\) 矩阵有解,所以可将我们这组完美匹配得到的列整列并在 \(n(m-1)\) 矩阵的答案中,得到 \(nm\) 矩阵的答案。

综上,\(n\)\(m\) 列,且满足对于 \(i \in [1,n]\) ,都恰好出现了 \(m\) 次的矩阵,一定有解。

我们从证明中可以发现,我们每次对于当前矩阵建图,找出一组完美匹配,并入答案并删去后得到新矩阵,这样肯定可以得到一组解。

对于找完美匹配,可以用最大流算法,也可以跑匈牙利算法。

Code

#include <bits/stdc++.h>
using namespace std;
const int inf = 1e7;
int n, m, a[105][105], Ans[105][105], S, T;
int firs[405], nex[40005], to[40005], w[40005], tot = 1, lq, rq;
void Add (int u, int v, int p){
    ++ tot;
    nex[tot] = firs[u];
    firs[u] = tot;
    to[tot] = v;
    w[tot] = p;
    ++ tot;
    nex[tot] = firs[v];
    firs[v] = tot;
    to[tot] = u;
    w[tot] = 0;
}
int cur[405], dep[405], Ansflow;
queue < int > Q;
bool inq[405];
bool Bfs (){
    for (int i = 1;i <= T;++ i){
        cur[i] = firs[i];
        dep[i] = inf;
        inq[i] = false;
    }
    dep[S] = 0;
    Q.push (S);
    inq[S] = true;
    while (! Q.empty ()){
        int u = Q.front ();
        Q.pop ();
        inq[u] = false;
        for (int e = firs[u];e;e = nex[e]){
            int v = to[e];
            if (dep[v] > dep[u] + 1 && w[e] > 0){
                dep[v] = dep[u] + 1;
                if (! inq[v])
                    Q.push (v);
                inq[v] = true;
            }
        }
    }
    return dep[T] != inf;
}
int Solve (int u, int flow){
    if (u == T){
        Ansflow += flow;
        return flow;
    }
    int used = 0, rlow;
    for (int e = cur[u];e;e = nex[e]){
        cur[u] = e;
        int v = to[e];
        if (dep[v] == dep[u] + 1 && w[e] > 0){
            rlow = Solve (v, min (flow - used, w[e]));
            if (rlow != 0){
                used += rlow;
                w[e] -= rlow;
                w[e ^ 1] += rlow;
                if (used == flow)
                    break;
            }
        }
    }
    return used;
}
void Dinic (){
    Ansflow = 0;
    while (Bfs ())
        Solve (S, inf);
}
int main (){
    scanf ("%d%d", &n, &m);
    S = n << 1 | 1;
    T = S + 1;
    for (int i = 1;i <= n;++ i)
        Add (S, i, 1);
    lq = tot;
    for (int i = 1;i <= n;++ i)
        for (int j = 1;j <= m;++ j){
            scanf ("%d", &a[i][j]);
            Add (i, n + a[i][j], 1);
        }
    rq = tot;
    for (int i = 1;i <= n;++ i)
        Add (n + i, T, 1);
    for (int i = 1;i <= m;++ i){
        Dinic ();
        if (Ansflow != n){
            // puts ("No");
            puts ("This_is_FFT");
            return 0;
        }
        for (int e = 2;e <= lq;e += 2){
            w[e] = 1;
            w[e ^ 1] = 0;
        }
        for (int e = rq + 1;e <= tot;e += 2){
            w[e] = 1;
            w[e ^ 1] = 0;
        }
        for (int e = lq + 1;e <= rq;++ e)
            if (w[e] > 0 && (e & 1)){
                Ans[to[e]][i] = to[e ^ 1] - n;
                w[e] = 0;
            }
    }
    puts ("Yes");
    for (int i = 1;i <= n;++ i){
        for (int j = 1;j <= m;++ j)
            printf ("%d ", Ans[i][j]);
        puts ("");
    }
    return 0;
}