[TJOI 2018] 智力竞赛
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=5335
[算法]
首先发现答案具有单调性 , 不妨二分答案mid
将所有权值小于mid的 , 且在原图上可以互相到达的点连边
那么我们需要判断新图的最小可重路径点覆盖是否 <= n + 1
直接网络流 / 匈牙利算法解决
时间复杂度 : O(M ^ 3logM)
[代码]
#include<bits/stdc++.h> using namespace std; #define N 510 typedef long long ll; typedef long double ld; typedef unsigned long long ull; int n , m , len; int w[N] , k[N] , a[N][N] , match[N] , vis[N] , b[N] , G[N][N] , mp[N][N]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline bool hungary(int u) { for (int i = 1; i <= m; ++i) { if (G[u][i]) { if (!vis[i]) { vis[i] = true; if (!match[i] || hungary(match[i])) { match[i] = u; return true; } } } } return false; } inline bool check(int mid) { for (int i = 1; i <= m; ++i) { for (int j = 1; j <= m; ++j) { G[i][j] = 0; } } for (int i = 1; i <= m; ++i) { for (int j = 1; j <= m; ++j) { if (w[i] < mid && w[j] < mid && mp[i][j]) G[i][j] = 1; } } for (int x = 1; x <= m; ++x) { for (int i = 1; i <= m; ++i) { if (!G[i][x]) continue; for (int j = 1; j <= m; ++j) { if (i != j) G[i][j] |= (G[i][x] & G[x][j]); } } } int res = 0; for (int i = 1; i <= m; ++i) if (w[i] < mid) ++res; for (int i = 1; i <= m; ++i) match[i] = vis[i] = 0; for (int i = 1; i <= m; ++i) { memset(vis , false , sizeof(vis)); if (w[i] < mid && hungary(i)) --res; } return res <= n + 1; } int main() { read(n); read(m); for (int i = 1; i <= m; ++i) { read(w[i]); read(k[i]); for (int j = 1; j <= k[i]; ++j) { read(a[i][j]); mp[i][a[i][j]] = true; } b[i] = w[i]; } for (int x = 1; x <= m; ++x) { for (int i = 1; i <= m; ++i) { if (!mp[i][x]) continue; for (int j = 1; j <= m; ++j) { if (i != j) mp[i][j] |= (mp[i][x] & mp[x][j]); } } } sort(b + 1 , b + m + 1); int l = 1 , r = m , ans = 0; while (l <= r) { int mid = (l + r) >> 1; if (check(b[mid])) { ans = b[mid]; l = mid + 1; } else r = mid - 1; } if (check(b[m] + 1)) printf("AK\n"); else printf("%d\n" , ans); return 0; }