• Solution 「CF 590E」Birthday


    \(\mathscr{Description}\)

      Link.

      给定 \(n\) 个字符串 \(S_{1..n}\),选出其一个最大子集 \(T\),使得 \(T\) 中的字符串两两不存在包含关系。要求构造答案。

      \(n\le750\)\(\sum|S|\le10^7\)

    \(\mathscr{Solution}\)

      \(\subseteq\)\(\{S_n\}\) 中是偏序关系,那么最长反链 \(T\) 的大小就是最小链覆盖大小。先建出 AC 自动机,通过 fail 树的相邻祖孙关系(每个点与其最深祖先的关系)建立偏序,再通过 bitset 维护传递闭包。利用经典二分图最大匹配模型就能求出 \(|T|\)

      难点在于 \(T\) 的构造。事实上,这就是普遍的构造最长反链的方法。推荐 C202044zxy 的博客。由于还没有想到 motivated 的讲解,所以直接搬前人的轮子了。(

      提炼一点结论性的过程方便复习:

    • 先求最大匹配。
    • 最大匹配 \(\rightarrow\) 最小点覆盖,二分图上 DFS。
    • 最小点覆盖 \(\rightarrow\) 最大独立集,直接取补集。
    • 实点和虚点都在独立集里的点选入反链。

      实现上,\(\mathcal O(n^3)\) Hungary 然后 DFS 标记一遍就行了,不需要想证明一样分很多步。(所以我还是觉得这个证明写得不自然。

    \(\mathscr{Code}\)

    /*+Rainybunny+*/
    
    #include <bits/stdc++.h>
    
    #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
    #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
    
    const int MAXN = 750, MAXL = 1e7, IINF = 0x3f3f3f3f;
    int n, mtc[MAXN * 2 + 5];
    bool vis[MAXN * 2 + 5];
    std::string str[MAXN + 5];
    std::bitset<MAXN + 5> adj[MAXN + 5];
    
    struct AhoCorasickAutomaton {
        int node, ch[MAXL + 5][2], len[MAXL + 5][2], fail[MAXL + 5], bel[MAXL + 5];
        int que[MAXL + 5], hd, tl;
        AhoCorasickAutomaton(): node(1) {}
    
        inline int insert(const std::string& str, const int id) {
            int u = 1;
            for (int c: str) {
                if (!ch[u][c -= 'a']) ch[u][c] = ++node;
                u = ch[u][c];
            }
            return bel[u] = id, u;
        }
    
        inline void build() {
            hd = 1, tl = 0;
            if (ch[1][0]) fail[que[++tl] = ch[1][0]] = 1;
            else ch[1][0] = 1;
            if (ch[1][1]) fail[que[++tl] = ch[1][1]] = 1;
            else ch[1][1] = 1;
            while (hd <= tl) {
                int u = que[hd++];
                if (ch[u][0]) fail[que[++tl] = ch[u][0]] = ch[fail[u]][0];
                else ch[u][0] = ch[fail[u]][0];
                if (ch[u][1]) fail[que[++tl] = ch[u][1]] = ch[fail[u]][1];
                else ch[u][1] = ch[fail[u]][1];
            }
        }
    } acam;
    
    struct DSU {
        int fa[MAXL + 5];
        inline int find(const int x) {
            return x == fa[x] ? x : fa[x] = find(fa[x]);
        }
    } dsu;
    
    inline bool augment(const int u) {
        rep (v, 1, n) if (adj[u].test(v) && !vis[v]) {
            vis[v] = true;
            if (!mtc[v + n] || augment(mtc[v + n])) {
                mtc[v + n] = u, mtc[u] = v + n;
                return true;
            }
        }
        return false;
    }
    
    inline void mark(const int u) {
        vis[u + n] = true;
        rep (v, 1, n) if (adj[v].test(u) && !vis[v]) {
            vis[v] = true, mark(mtc[v] - n);
        }
    }
    
    int main() {
        // freopen("a.in", "r", stdin);
        // freopen("a.out", "w", stdout);
        std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    
        std::cin >> n;
        rep (i, 1, n) std::cin >> str[i], acam.insert(str[i], i);
        acam.build();
    
        rep (i, 1, acam.node) {
            if (i != 1 && !acam.bel[i]) dsu.fa[i] = acam.fail[i];
            else dsu.fa[i] = i;
        }
    
        rep (i, 1, n) {
            int u = 1;
            for (int c: str[i]) {
                u = acam.ch[u][c - 'a'];
                if (acam.bel[dsu.find(u)]) adj[i].set(acam.bel[dsu.find(u)]);
            }
            u = acam.fail[u]; // When bel[u]=i, there's some thing wrong. Fix it.
            if (acam.bel[dsu.find(u)]) adj[i].set(acam.bel[dsu.find(u)]);
        }
        rep (k, 1, n) rep (i, 1, n) if (adj[i].test(k)) adj[i] |= adj[k];
        rep (i, 1, n) adj[i].reset(i);
    
        int ans = n;
        rep (i, 1, n) ans -= augment(i), memset(vis, false, sizeof vis);
        rep (i, 1, n) if (!mtc[i + n]) mark(i);
    
        std::cout << ans << '\n';
        rep (i, 1, n) if (!vis[i] && vis[i + n]) std::cout << i << ' ';
        std::cout << '\n';
        return 0;
    }
    
    
  • 相关阅读:
    test example
    SSD: ReLU6
    jupyter
    ubuntu+anaconda
    linux动态库
    ssd制作数据和训练
    ncnn框架
    Lock->ReentrantLock->ReentrantReadWriteLock简介
    Synchronized简介与原理
    ThreadLocal简介与原理
  • 原文地址:https://www.cnblogs.com/rainybunny/p/16368829.html
Copyright © 2020-2023  润新知