[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;
    
}

 

posted @ 2019-03-16 23:02  evenbao  阅读(223)  评论(0编辑  收藏  举报