智力竞赛
题目背景:
5.21 模拟 TJOI2018D1T2
分析:floyed + 二分 + 可重点最小链覆盖
这道题的题意很奇葩,本质一点就是有一个有向图可以选择n + 1条链,点可以重复,问覆盖不到的权值中最小值最大是多少?
因为看原题担心会有环,还判了环,结果最后发现全部都是dag也不是很想说什么,因为是可重点,我们先用floyed跑一遍传递闭包,那么最小链覆盖就是直接进行二分图匹配,总点数 - 匹配数就是最小链覆盖了,直接二分覆盖不到的最小权值,然后判断前面的点可不可以用n + 1条链覆盖完全就可以了。复杂度O(m3logv),离散化之后logv几乎是常数,再加上匈牙利的二分图匹配也远没有到m3的上界,所以可以比较轻松的跑过。
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <deque>
#include <iterator>
#include <map>
inline char read() {
static const int IN_LEN = 1024 * 1024;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}
// /*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}
inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout);
}
const int MAXN = 500 + 10;
int n, m, k, x, cnt, top, rk[MAXN];
int match[MAXN], father[MAXN], v[MAXN], id[MAXN];
bool g[MAXN][MAXN], vis[MAXN], f[MAXN][MAXN];
std::vector<int> edge[MAXN];
struct data {
int num, ori;
data() {}
data(int num, int ori) : num(num), ori(ori) {}
inline bool operator < (const data &a) const {
return num < a.num;
}
} a[MAXN];
bool find(int cur) {
for (int p = 0; p < edge[cur].size(); ++p) {
int v = edge[cur][p];
if (v && !vis[v]) {
vis[v] = true;
if (!match[v] || find(match[v]))
return match[v] = cur, true;
}
}
return false;
}
inline bool check(int x) {
int cnt = 0;
for (int i = 1; i <= n; ++i) if (v[i] < x) ++cnt, edge[i].clear();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (v[i] < x && v[j] < x && f[i][j]) edge[i].push_back(j);
for (int i = 1; i <= n; ++i) match[i] = 0;
for (int i = 1; i <= n; ++i)
if (v[i] < x) {
memset(vis, false, sizeof(bool) * (n + 2));
if (find(i)) cnt--;
}
return (cnt <= m + 1);
}
inline void solve() {
R(m), R(n);
for (int i = 1; i <= n; ++i) {
R(v[i]), R(k);
while (k--) R(x), g[i][x] = true;
}
for (register int k = 1; k <= n; ++k)
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= n; ++j)
g[i][j] |= (g[i][k] && g[k][j]);
for (int i = 1; i <= n; ++i) father[i] = i;
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
if (g[i][j] && g[j][i])
father[j] = i, v[i] = std::min(v[i], v[j]);
for (int i = 1; i <= n; ++i)
if (father[i] == i) id[i] = ++cnt, v[cnt] = v[i];
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= n; ++j)
if (id[i] && id[j] && id[i] != id[j])
f[id[i]][id[j]] = g[i][j];
n = cnt;
for (int i = 1; i <= n; ++i) a[i] = data(v[i], i);
std::sort(a + 1, a + n + 1);
int i = 1;
while (i <= n) {
int j = i;
while (i < n && a[i + 1].num == a[i].num) ++i;
rk[++top] = a[i].num;
for (int k = j; k <= i; ++k) v[a[k].ori] = top;
++i;
}
int l = 1, r = top + 2;
while (l + 1 < r) {
int mid = l + r >> 1;
(check(mid)) ? l = mid : r = mid;
}
if (l <= top) std::cout << rk[l];
else std::cout << "AK";
}
int main() {
// clock_t start = clock();
freopen("contest.in", "r", stdin);
freopen("contest.out", "w", stdout);
solve();
// std::cerr << (double)(clock() - start) / CLOCKS_PER_SEC << '\n';
return 0;
}