[SCOI2015]小凸玩矩阵

壹、题目

传送门

贰、思考

考虑二分答案,现在要使得我们选出来的数是第 \(k\) 大,就需要选出 \(k-1\) 个大于等于它的数字,并且剩下的数字还得小于等于它,怎么才能满足这俩条件?

其实只需要考虑比它小的数字,因为我们是二分,所以如果我们二分出一个 \(x\),如果无论怎么选都会有大于等于 \(k\) 个小于等于它的,那么我们自然要将 \(x\) 值调小,直到某个时刻最多也只有 \(k\) 个数字小于等于它,这个时候 \(x\) 是一定满足条件的。

叁、代码

using namespace Elaina;

const int maxn=250;

int n,m,k;
int a[maxn+5][maxn+5];

int t[maxn*maxn+5],cnt;

inline void input(){
    n=readin(1),m=readin(1),k=readin(1);
    rep(i,1,n)rep(j,1,m){
        a[i][j]=readin(1);
        t[++cnt]=a[i][j];
    }
}

struct edge{int to,nxt;}e[maxn*maxn+5];
int tail[maxn+5],ecnt;
inline void add_edge(const int u,const int v){
    // printf("add_edge :> %d to %d\n",u,v);
    e[++ecnt]=edge{v,tail[u]};tail[u]=ecnt;
}
inline void build(const int x){
    // printf("build :> x == %d\n",x);
    memset(tail+1,0,n<<2);ecnt=0;
    rep(i,1,n)rep(j,1,m)if(a[i][j]<=x)
        add_edge(i,j);
}

int vis[maxn+5],match[maxn+5];
int augment(const int u){
    for(int i=tail[u],v;i;i=e[i].nxt)if(!vis[v=e[i].to]){
        vis[v]=1;
        if(!match[v] || augment(match[v])){
            match[v]=u;
            return 1;
        }
    }
    return 0;
}
inline int hungary(){
    int ret=0;
    memset(match+1,0,m<<2);
    rep(i,1,n){
        memset(vis+1,0,m<<2);
        if(augment(i))++ret;
    }
    return ret;
}

inline void bisearch(){
    int l=1,r=1e9,mid,ans;
    while(l<=r){
        mid=(l+r)>>1;
        // printf("Now l == %d, r == %d\n",l,r);
        build(mid);
        if(hungary()>=(n-k+1))ans=mid,r=mid-1;
        else l=mid+1;
    }
    writc(ans,'\n');
}

signed main(){
    input();
    bisearch();
    return 0;
}

用到の小 \(\tt trick\)

二分答案的自适应性,这个需要好好体会。

posted @ 2021-02-04 21:18  Arextre  阅读(60)  评论(0编辑  收藏  举报