CF590E Birthday

\(\text{Solution}\)

建出 ACAM 后利用 fail 树就可以确定子串关系了,如果建成有向图
然后看问题,考虑最长反链等于最小链覆盖,那么就是求一个可重路径覆盖问题
Floyd 传递闭包后变成不可重路径覆盖,拆点二分图就有最小路径覆盖等于总点数减最大匹配
考虑构造方案,本质上是个传递闭包后的最大独立集
找到残量网络上由 \(S\) 出发能走到的点,那么左部不能到的点和右部能到的点构成了最小点覆盖
取补集就是最大独立集了

\(\text{Code}\)

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

template <typename Tp>
void read(Tp &x) {
	x = 0; char ch = getchar(); int f = 0;
	for(; !isdigit(ch); f = (ch == '-' ? 1 : f), ch = getchar());
	for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
	if (f) x = ~x + 1;
}

const int N = 755, M = 1e7 + 5;
int n;
char str[M];

struct Network {
    int tot, h[N << 2], cur[N << 2], T, dep[N << 2], vis[N << 2];
    struct edge{int to, nxt, w;}e[N * N << 2];
    void init() {
        tot = 1, T = n + n + 1;
        for(int i = 0; i <= T; i++) h[i] = 0;
    }
    void add(int x, int y, int z) {
        e[++tot] = edge{y, h[x], z}, h[x] = tot;
        e[++tot] = edge{x, h[y], 0}, h[y] = tot;
    }
    queue<int> Q;
    int bfs() {
        for(int i = 0; i <= T; i++) dep[i] = 0, cur[i] = h[i];
        Q.push(0), dep[0] = 1;
        while (!Q.empty()) {
            int now = Q.front(); Q.pop();
            for(int i = h[now]; i; i = e[i].nxt) {
                int v = e[i].to;
                if (dep[v] || !e[i].w) continue;
                dep[v] = dep[now] + 1, Q.push(v);
            }
        }
        return dep[T];
    }
    int dfs(int x, int lim) {
        if (lim <= 0 || x == T) return lim;
        int flow = 0;
        for(int i = cur[x], v, f; i; i = e[i].nxt) {
            cur[x] = i, v = e[i].to;
            if (dep[v] != dep[x] + 1 || !e[i].w) continue;
            f = dfs(v, min(lim, e[i].w));
            if (f <= 0) continue;
            e[i].w -= f, e[i ^ 1].w += f, flow += f, lim -= f;
            if (lim <= 0) break;
        }
        return flow;
    }
    int dinic(){int flow = 0; while (bfs()) flow += dfs(0, N); return flow;}
    void dfs(int x) {
        vis[x] = 1;
        for(int i = h[x]; i; i = e[i].nxt) if (e[i].w && !vis[e[i].to]) dfs(e[i].to);
    }
    void work() {
        dfs(0);
        for(int i = 1; i <= n; i++)
            if (!(!vis[i] || vis[i + n])) printf("%d ", i);
    }
}nt;

struct Graph {
    int g[N][N];
    void Floyd() {
        for(int k = 1; k <= n; k++)
            for(int i = 1; i <= n; i++) if (i ^ k)
                for(int j = 1; j <= n; j++) if ((j ^ i) && (j ^ k))
                    g[i][j] |= g[i][k] & g[k][j];
    }
    void build() {
        nt.init();
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) if (g[i][j]) nt.add(i, j + n, 1);
        for(int i = 1; i <= n; i++) nt.add(0, i, 1);
        for(int i = 1; i <= n; i++) nt.add(i + n, nt.T, 1);
    }
}G;

struct ACAM {
    int id[M], size, tr[M][2], fail[M], ed[N], fa[M];
    void insert(int x, int len)  {
        int u = 0, ch;
        for(int i = 1; i <= len; i++) {
            ch = str[i] - 'a';
            if (!tr[u][ch]) tr[u][ch] = ++size;
            fa[tr[u][ch]] = u, u = tr[u][ch];
        }
        id[u] = x, ed[x] = u;
    }
    void getfail() {
        queue<int> Q;
        for(int i = 0; i < 2; i++) if (tr[0][i]) Q.push(tr[0][i]);
        while (!Q.empty()) {
            int now = Q.front(); Q.pop();
            for(int i = 0; i < 2; i++) {
                if (tr[now][i]) fail[tr[now][i]] = tr[fail[now]][i], Q.push(tr[now][i]);
                else tr[now][i] = tr[fail[now]][i];
            }
        }
    }
    void build() {
        vector<int> Q;
        for(int i = 1; i <= n; i++) {
            for(int j = ed[i]; j; j = fa[j]) {
                int x = fail[j];
                while (x && !id[x]) Q.eb(x), x = fail[x];
                while (!Q.empty()) fail[Q.back()] = x, Q.pop_back();
                fail[j] = x;
                if (j != ed[i] && id[j]) x = j;
                if (x) G.g[i][id[x]] = 1;
            }
        }
    }
}ac;

int main() {
    read(n);
    for(int i = 1; i <= n; i++) scanf("%s", str + 1), ac.insert(i, strlen(str + 1));
    ac.getfail(), ac.build(), G.Floyd(), G.build();
    printf("%d\n", n - nt.dinic()), nt.work();
}
posted @ 2023-03-15 21:26  leiyuanze  阅读(18)  评论(0编辑  收藏  举报