题意:给出n个人,m个社团,每个人都有一个标号,一个能力值,并且属于一个社团,第i天的凌晨,第$k_i$个人会离开。每天每个社团最多派一个人出来参加活动。派出的人的能力值集合为S,求每天$MEX{S}$的最大值。
思路:这道题正着删人和倒着加人是一样的,并且很容易看出是二分图的题,加边显然要比删边容易操作,所以我们要倒着考虑。
只要想到了倒着考虑,剩下的就比较好想了。由于从小到大的能力值是必选的,我们把能力值作为左边的节点,社团作为右边的节点。每增加一个人就增加对应的边,每次匹配的时候上次的答案开始匹配,匹配到不能匹配位置,就是当前的答案了。由于二分图的特殊性,将当前这个点匹配了,肯定不会使之前的点变成失配点。
要注意的是,这里人的能力值最低是0,所以$match[x]=0$也是一个合法匹配,而大部分匈牙利算法的模板中都使用$match[x]==0$来作为有没有匹配过的判断条件,要注意修改。
#pragma GCC optimize (2) #pragma G++ optimize (2) #pragma comment(linker, "/STACK:102400000,102400000") #include<bits/stdc++.h> #include<cstdio> #include<vector> #define rep(i,a,b) for(int i=a;i<=b;i++) #define dep(i,b,a) for(int i=b;i>=a;i--) #define clr(a,b) memset(a,b,sizeof(a)) #define pb push_back #define pii pair<int,int > using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; ll rd() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=5010; const ll mod=1e9+7; int n,m; vector<int >ve[maxn]; int match[maxn],vis[maxn]; bool dfs(int u){ for(auto &v:ve[u]){ if(!vis[v]){ vis[v]=1; if(match[v]==-1||dfs(match[v])){ match[v]=u; return true; } } } return false; } struct node{ int pos,val; }arr[maxn]; int k[maxn],num[maxn],ans[maxn]; int main(){ cin>>n>>m; rep(i,1,n){ arr[i].val=rd(); } rep(i,1,n){ arr[i].pos=rd(); } int d; cin>>d; rep(i,1,d){ k[i]=rd(); num[k[i]]=1; } clr(match,-1); rep(i,1,n){ if(!num[i]){ ve[arr[i].val].push_back(arr[i].pos); } } int pos=0; dep(i,d,1){ while(1){ clr(vis,0); if(dfs(pos)){ pos++; }else{ break; } } ans[i]=pos; ve[arr[k[i]].val].push_back(arr[k[i]].pos); } rep(i,1,d){ printf("%d ",ans[i]); } }