20200723 SCOI模拟T3(网络流)

T3在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:
二分答案 mid
找到一个串的所有长度小于 mid 子序列
发现一个性质
一个串的不同子序列个数大于 n,那么一定有匹配
所以一个串搜出 n 个子序列后就不用搜了
对于每个串和子序列建点,每个串向它包含的子序列连边,二分图匹配即可

搜索要优化剪枝

代码:

#include <bits/stdc++.h>

#include <tr1/unordered_map>
#include <tr1/unordered_set>
using namespace std;
typedef unsigned long long ull;
#define re register
namespace IO {
inline char ch() {
  static char buf[1 << 21], *p1 = buf, *p2 = buf;
  return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
             ? EOF
             : *p1++;
}
inline int in() {
  int s = 0, f = 1;
  char x;
  for (x = getchar(); x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
}  // namespace IO
using namespace IO;

const int A = 500;
const int P = 37;
const int N = 5e5 + 5;
const int INF = 1e9;
int n;
char nm[A][A];
int len[A];
int tot, cor;
int head[N], cur[N], tot_road = 1;
struct Road {
  int nex, to, fw;
} road[2 * N];
inline void edge(int x, int y, int w) {
  road[++tot_road] = {head[x], y, w};
  head[x] = tot_road;
  road[++tot_road] = {head[y], x, 0};
  head[y] = tot_road;
  return;
}

tr1::unordered_map<ull, int> t;
tr1::unordered_set<ull> rep;
int st[N], top;

inline void find(int now, int x, ull val, int num) {
  if (top > n) return;
  if (!num) return;
  for (re int i = x + 1; i <= len[now]; ++i) {
    ull w = val * P + (nm[now][i] - 'a' + 1);
    if (rep.find(w) == rep.end()) {
      st[++top] = w;
      rep.insert(w);
      find(now, i, w, num - 1);
    }
  }
  return;
}

#define S 0
#define T n + tot + 1
inline void build(int k) {
  for (re int i = 1; i <= n; ++i) {
    rep.clear();
    top = 0;
    find(i, 0, 0, k);
    if (top > n) {
      cor++;
      continue;
    }
    for (re int j = 1; j <= top; ++j) {
      ull val = st[j];
      if (t.find(val) == t.end()) t[val] = ++tot;
      edge(i, n + t[val], INF);
    }
  }
  for (re int i = 1; i <= n; ++i) edge(S, i, 1);
  for (re int i = 1; i <= tot; ++i) edge(n + i, T, 1);
  return;
}

int maxflow = 0;
int dep[N], sum[N];
inline void BFS() {
  for (re int i = S; i <= T; ++i) dep[i] = -1, sum[i] = 0;
  queue<int> q;
  dep[T] = 0, sum[dep[T]]++;
  q.push(T);
  while (!q.empty()) {
    int x = q.front();
    q.pop();
    for (re int y = head[x]; y; y = road[y].nex) {
      int z = road[y].to;
      if (dep[z] != -1) continue;
      dep[z] = dep[x] + 1, sum[dep[z]]++;
      q.push(z);
    }
  }
  return;
}
inline int DFS(int x, int flow) {
  if (x == T) {
    maxflow += flow;
    return flow;
  }
  int used = 0;
  for (re int &y = cur[x]; y; y = road[y].nex) {
    int z = road[y].to, w = road[y].fw;
    if (w && dep[z] == dep[x] - 1) {
      int after = DFS(z, min(w, flow - used));
      if (after) {
        used += flow;
        road[y].fw -= after;
        road[y ^ 1].fw += after;
      }
    }
    if (used == flow) return used;
  }
  if (!--sum[dep[x]]) dep[T] = n + 1;
  sum[++dep[x]]++;
  return used;
}
inline void ISAP() {
  maxflow = 0;
  BFS();
  while (dep[T] <= n) {
    for (int i = S; i <= T; i++) cur[i] = head[i];
    DFS(S, INF);
  }
  return;
}

inline void clean() {
  t.clear();
  for (re int i = S; i <= T; ++i) head[i] = 0;
  tot = cor = 0;
  tot_road = 1;
  return;
}

inline int check(int k) {
  clean();
  build(k);
  ISAP();
  if (maxflow == n - cor) return 1;
  return 0;
}

signed main() {
  n = in();
  for (re int i = 1; i <= n; ++i) {
    scanf("%s", nm[i] + 1);
    len[i] = strlen(nm[i] + 1);
  }
  int L = 1, R = 300, ans = -1;
  while (L <= R) {
    int mid = (L + R) >> 1;
    if (check(mid))
      R = mid - 1, ans = mid;
    else
      L = mid + 1;
  }
  printf("%d\n", ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值