[Codeforces 590E] Birthday
来源:互联网 发布:建筑图纸设计软件 编辑:程序博客网 时间:2024/06/07 03:32
题目链接: http://codeforces.com/problemset/problem/590/E
题意: 给出n个字符串, 从中取出最多的字符串满足彼此之间互不为其子串, 输出最多能取多少个和任意一种合法方案。(
思路: 字符串之间的包含关系图是一个DAG, 且满足传递性, 答案即对构图后求最大独立点集, 即将DAG中的点按入度出度拆点后, 化成二分图后, 答案为n - 最大匹配, 可以考虑用Dinic求, 当然也可以直接匈牙利。 方案是残留网络中该点与源点连通且与之对应的另一个点不与汇点连通的点。
考虑用AC自动机建图,由于该题保证了每个字符串互不相同, 一开始每个结束节点存上对应的字符串编号, 在求fail链时, 对于不是结束节点的节点, 通过fail继承它所包含的最近的字符串即可。 再将每个字符串在AC自动机中跑一边, 得到一个初始的包含关系图, 由于包含关系具有传递性, 对该图跑一边Floyd算法, 即可获得完整的关系图, 可以继续后面的二分图匹配过程。
PS:该题用Dinic跑的话还要加当前弧优化才不会TLE
#include <queue>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = (int)1e7 + 10;const int M = 800;const int inf = 1e8;int n;char str[N]; int st[M], ed[M];int rt, tot, ch[N][2], fail[N], fa[N], pre[N], num[N];void insert(int id){ int u = rt; for (int i = st[id]; i <= ed[id]; i ++){ int nxt = str[i] - 'a'; if (!ch[u][nxt]) ch[u][nxt] = ++ tot, fa[tot] = u, pre[tot] = nxt; u = ch[u][nxt]; if (i == ed[id]) num[u] = id; }}int head, tail, que[N]; void call_fail(){ head = tail = 0; que[tail = 1] = rt; while (head < tail){ int u = que[++ head]; for (int i = 0; i < 2; i ++) if (ch[u][i]) que[++ tail] = ch[u][i]; int p = fa[u]; while (u != rt && p != rt && !ch[fail[p]][pre[u]]) p = fail[p]; if (p == rt) continue; fail[u] = ch[fail[p]][pre[u]]; if (!num[u]) num[u] = num[fail[u]]; }}int s, t, G[M][M];int cnt = 1, lst[M * 2], cur[M * 2], nxt[N], to[N], f[N];void add(int u, int v, int flow){ nxt[++ cnt] = lst[u]; lst[u] = cnt; to[cnt] = v; f[cnt] = flow; nxt[++ cnt] = lst[v]; lst[v] = cnt; to[cnt] = u; f[cnt] = 0;}void link(int id){ int u = rt; for (int i = st[id]; i <= ed[id]; i ++){ int nxt = str[i] - 'a'; u = ch[u][nxt]; int tmp = i != ed[id] ? u : fail[u]; if (num[tmp]){ G[id][num[tmp]] = 1; } }}int d[N];bool bfs(){ head = tail = 0; for (int i = 1; i <= t; i ++) d[i] = 0; que[tail = 1] = s; d[s] = 1; while (head < tail){ int u = que[++ head]; for (int j = lst[u]; j; j = nxt[j]){ int v = to[j]; if (!d[v] && f[j]){ d[v] = d[u] + 1; que[++ tail] = v; if (v == t) return 1; } } } return 0;}int dfs(int u, int flow){ if (u == t) return flow; int ret = 0, a; for (int &j = cur[u]; j; j = nxt[j]){ int v = to[j]; if (d[v] == d[u] + 1 && flow && f[j] && (a = dfs(v, min(flow, f[j])))){ ret += a, f[j] -= a; flow -= a, f[j ^ 1] += a; } } return ret;}int main(){ scanf("%d", &n); for (int i = 1; i <= n; i ++){ st[i] = ed[i - 1] + 1; scanf("%s", str + st[i]); ed[i] = strlen(str + st[i]) + st[i] - 1; insert(i); } call_fail(); for (int i = 1; i <= n; i ++) link(i); 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 != k && j != i) G[i][j] |= (G[i][k] && G[k][j]); for (int i = 1; i <= n; i ++) for (int j = 1; j <= n; j ++) if (G[i][j] && i != j) add(i, j + n, 1); s = n * 2 + 1, t = s + 1; for (int i = 1; i <= n; i ++) add(s, i, 1), add(i + n, t, 1); int ans = n; while (bfs()){ for (int i = 1; i <= t; i ++) cur[i] = lst[i]; ans -= dfs(s, inf); } printf("%d\n", ans); for (int i = 1; i <= n; i ++) if (d[i] && !d[i + n]) printf("%d ", i); return 0;}
阅读全文
0 0
- [Codeforces 590E] Birthday
- CodeForces 248E Piglet's Birthday (概率)
- codeforces 718E. Matvey's Birthday
- Codeforces 439E Devu and Birthday Celebration(计数问题)
- codeforces Round#369 div2-E ZS and The Birthday Paradox
- CodeForces 369 div2 E. ZS and The Birthday Paradox 数论
- Codeforces 711E ZS and The Birthday Paradox(数学)
- Codeforces 711E. ZS and The Birthday Paradox
- Codeforces 711E ZS and The Birthday Paradox
- 【28.57%】【codeforces 711E】ZS and The Birthday Paradox
- Codeforces Round #327 (Div. 1) E. Birthday【AC自动机+网络流】
- cf/codeforces#369-E - ZS and The Birthday Paradox- 数学+gcd+逆元+勒让德定理
- [Codeforces #369 (Div. 2)E. ZS and The Birthday Paradox]勒让德定理+逆元
- Codeforces #369 (Div. 2) E. ZS and The Birthday Paradox (勒让德定理+逆元)
- CodeForces-711E ZS and The Birthday Paradox(勒让德定理+抽屉原理)
- codeforces Round#369 div2-E ZS and The Birthday Paradox(逆元,组合)★
- [Codeforces 711E] ZS and The Birthday Paradox (数学+Legendre公式)
- Codeforces 711E ZS and The Birthday Paradox 数论(Legendre's定理)
- 欢迎使用CSDN-markdown编辑器
- 【lower_bound】【upper_bound】二分查找
- 集训8.9
- 函数指针
- 匿名对象和匿名类
- [Codeforces 590E] Birthday
- 凸包模板
- oracle单引号和双引号的区别
- 接口与多态
- Truck History(最小生成树)
- 自己初步了解python数据爬虫
- Hdu 4003 Find Metal Mineral 树型背包DP
- 有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数), 凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
- 找规律 permutation