BZOJ5335:[TJOI2018]智力竞赛——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=5335

小豆报名参加智力竞赛,他带上了n个好朋友作为亲友团一块来参加比赛。
比赛规则如下:
一共有m道题目,每个入都有1次答题机会,每次答题为选择一道题目回答,在回答正确后,可以从这个题目的后续题目,直达题目答错题目或者没有后续题目。每个问题都会代表一个价值,比赛最后的参赛选手获得奖励价值等价于该选手和他的亲友团没有回答的问题中的最低价值。我们现在知道小豆和他的亲友团实力非常强,能够做出这次竞赛中的所有题目。
小豆想知道在知道题目和后续题目的条件下,他最大能获得价值是多少?

原来两点可达的floyd求法是传递闭包啊……

我们floyd求出每个点之间是否可达,然后根据这个建边,之后跑一遍最小路径覆盖即可,答案为节点数-最大匹配数。

那么对答案二分,则只有两点都小于答案的点才可以连边,跑一遍即可。

#include<cmath>
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=550;
const int M=N*N;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int to,nxt;
}e[M];
int vis[N],head[N],shu[N],cnt;
int w[N],mp[N][N],d[N][N],maxn,n,m;
inline void add(int u,int v){
    e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
bool dfs(int u,int id){
    for(int i=head[u];i;i=e[i].nxt){
    int v=e[i].to;
    if(vis[v]!=id){
        vis[v]=id;
            if(!shu[v]||dfs(shu[v],id)){
                shu[v]=u;
                return 1;
            }
    }
    }
    return 0;
}
inline void init(){
    cnt=0;
    memset(shu,0,sizeof(shu));
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
}
bool pan(int val){
    init();
    for(int i=1;i<=m;i++){
    for(int j=1;j<=m;j++){
        if(mp[i][j]&&w[i]<val&&w[j]<val)d[i][j]=1;
    }
    }
    for(int k=1;k<=m;k++){
    for(int i=1;i<=m;i++){
        if(!d[i][k])continue;
        for(int j=1;j<=m;j++){
        d[i][j]|=d[i][k]&d[k][j];
        }
    }
    }
    int ans=0;
    for(int i=1;i<=m;i++){
    if(w[i]<val)ans++;
    else continue;
    for(int j=1;j<=m;j++){
        if(d[i][j])add(i,j);
    }
    }
    for(int i=1;i<=m;i++){
    if(w[i]>=val)continue;
    if(dfs(i,i))ans--;
    }
    return ans<=n+1;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
    w[i]=read();int k=read();
    maxn=max(maxn,w[i]);
    for(int j=1;j<=k;j++){
        int v=read();
        mp[i][v]=1;
    }
    }
    for(int k=1;k<=m;k++){
    for(int i=1;i<=m;i++){
        if(!mp[i][k])continue;
        for(int j=1;j<=m;j++){
        if(i==j)continue;
        mp[i][j]|=mp[i][k]&mp[k][j];
        }
    }
    }
    int l=1,r=maxn+1;
    while(l<r){
    int mid=(l+r+1)>>1;
    if(pan(mid))l=mid;
    else r=mid-1;
    }
    if(l==maxn+1)puts("AK");
    else printf("%d\n",l);
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

猜你喜欢

转载自www.cnblogs.com/luyouqi233/p/9065606.html
今日推荐