Codeforces Round #548 (Div. 2) E.Maximize Mex

题意:n(5000)个学生,m个社团(5000),每个学生有一个值ai和归属的社团bi,现在领导想让不同社团出一个人,使得出来的人的值可以从0连续排到ans。每天有一个学生会退出自己的社团,问当天的最大ans是多少。
思路:

  1. 如果倒着来的话,那么就成别人进社团了,这样想好像问题简单一些。
  2. 我们把学生的值作为二分图的一个集合,社团作为二分图的一个集合,这样当天的最大值就成了二分图最大匹配问题。
  3. 既然我们倒着来,那么ans是可以dp推上去的,既上一次的ans为新一次的ans最小答案,这样复杂度的问题就解决了。
    代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0)
const int maxn = 5005;

int head[maxn],a[maxn],b[maxn],del[maxn],mat[maxn],tot;
bool vis[maxn],viss[maxn];
struct edge{
    int v,nex;
}e[maxn];
void add(int u,int v){
    e[++tot] = {v,head[u]},head[u] = tot;
}
bool find(int u){
    for(int i =head[u];i;i = e[i].nex){
        int v = e[i].v;
        if(vis[v]) continue;
        vis[v] = 1;
        if(!mat[v]||find(mat[v])){
            mat[v] = u;
            return 1;
        }
    }    
    return 0;
}
int main(){
    IO;
    int n,m;cin>>n>>m;  
    for1(i,n){
        cin>>a[i];
        a[i]++;
    }
    for1(i,n) cin>>b[i];
    int k;cin>>k;
    for1(i,k){
        cin>>del[i];
        //cerr<<"@!#!@#    "<<del[i]<<'
';
        viss[del[i]] = 1;
    }
    for1(i,n)if(!viss[i]){
        add(a[i],b[i]);
    }
    int ans = 1;stack<int>s;
    for(int i = k;i>=1;i--){
        for(;ans<=5001;ans++){ 
            memset(vis,0,sizeof(vis));
            if(!find(ans)) break;
        }
        s.push(ans-1);
        //cerr<<"!@#!@#@!#   "<<del[i]<<' '<<vis[del[i]]<<'
';
        if(viss[del[i]]){
            //cerr<<a[del[i]]<<' '<<b[del[i]]<<'
';
            viss[del[i]] = 0;
            add(a[del[i]],b[del[i]]);
        }
    }       
    //cout<<'
';
    while(!s.empty()){
        cout<<s.top()<<'
';
        s.pop();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/AlexPanda/p/12520284.html