E. Maximize Mex(匈牙利)

传送门
非常巧妙的变形题,契合了模板。
对于二分图的左部,采取 p i p_i pi作为点。右部,则采用 c i c_i ci作为点.
为什么这样做呢,左右互换不行吗?
其实是考虑到匈牙利是从左部的点开始dfs看有无增光路的。
考虑逆推,即从最后一个询问的答案开始计算,可以知道此时二分图中的边最少。
首先假定答案为0,那么无匹配边,此时我们只能加入 p i < = 0 p_i<=0 pi<=0的点,如果dfs产生了增光路,那么ans++,同时加入 p i < = a n s p_i<=ans pi<=ans的点。
这样可以保证时间复杂度是 n 2 + n ∗ d n^2+n*d n2+nd左右。

#include <bits/stdc++.h>
using namespace std;
#define MAXN 10500
#define ll long long
int p[MAXN];
int c[MAXN];
int ban[MAXN];
int n,m;
stack<int>add;
struct{
    int to;
    int val;
    int nxt;
}edge[MAXN*2];
int edgecnt=1;
int head[MAXN];
void addedge(int u,int v,int w)
{
    edge[++edgecnt].to=v;
    edge[edgecnt].val=w;
    edge[edgecnt].nxt=head[u];
    head[u]=edgecnt;
}
int vis[MAXN];
int match[MAXN];
int dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int to=edge[i].to;
        if(vis[to])continue;
        vis[to]=1;
        if(match[to]==-1||dfs(match[to]))
        {
            match[to]=x;
            return 1;
        }
    }
    return 0;
}
int ans=0;
vector<int>ed[MAXN];
int main()
{
    cin>>n>>m;
    memset(match,-1, sizeof(match));
    for(int i=1;i<=n;i++)
        scanf("%d",p+i);
    for(int i=1;i<=n;i++)
        scanf("%d",c+i);
    int d;scanf("%d",&d);
    for(int i=1;i<=d;i++)
    {
        int x;
        scanf("%d",&x);
        add.push(x);
        ban[x]=1;
    }

    for(int i=1;i<=n;i++){
        if(!ban[i]&&p[i]<=m)
        {
            if(p[i]<=0)
                addedge(p[i],c[i]+m,1);
            else
                ed[p[i]].push_back(i);
        }
    }
    vector<int>a;
    for(int i=1;i<=d;i++)
    {
        memset(vis,0, sizeof(vis));
        while(ans<m&&dfs(ans)){
            memset(vis,0, sizeof(vis));
            ans++;
            for(int j=0;j<ed[ans].size();j++) {
                int x=ed[ans][j];
                addedge(p[x], c[x] + m, 1);
            }
        }
        int cur=add.top();
        add.pop();
        if(p[cur]<=m)
        {
            if(p[cur]<=ans)
                addedge(p[cur], c[cur] + m, 1);
            else
                ed[p[cur]].push_back(cur);
        }
        a.push_back(ans);
    }
    for(int i=d-1;i>=0;i--)
        cout<<a[i]<<endl;

}

你可能感兴趣的:(二分图匹配)