题意

n n n个人,每个人有一个属性值 c i c_i ci,并属于唯一的一个俱乐部 b i b_i bi,有 d d d天,每天指定一个人离开他所属的俱乐部(以后不再回来),离开后,每一个俱乐部每天要推举一个人,每天被推举上来的人的属性值构成序列 S S S,要使 S S S m e x mex mex值最大,输出每天的 m e x mex mex最大值。

mex是指在一个序列中没有出现的最小自然数,在博弈论的SG函数中用到。
例如: [ 0 , 1 , 2 , 4 ] [0,1,2,4] [0,1,2,4] m e x mex mex值为 3 3 3 [ 1 , 2 , 3 ] [1,2,3] [1,2,3] m e x mex mex值为 0 0 0

题解

这是一个匹配问题。相当于每一个俱乐部匹配一个属性值,亦或是每一个属性值匹配一个俱乐部,要使 m e x mex mex最大,匹配数一定是越来越多的。因为 m e x mex mex本身的性质,我们要从 0 0 0开始,给每一个属性值匹配一个俱乐部,如果匹配到某个属性值 x x x时,无法匹配到俱乐部时, m e x mex mex就得到了。

建好‘属性值-俱乐部’的图之后,用KM算法匹配即可。

由于每天都有人离开,所以图是不一样的,暴力求解会超时。发现随着人越走越多,答案会越来越小。逆向考虑,时间从后往前,人越来越多。新的图的 m e x mex mex值一定是大于等于旧图的 m e x mex mex值的,所以,这样就可以利用之前匹配的结果,在此基础上,看能否增加匹配即可。

代码

#include<bits/stdc++.h>
#define N 50010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define P 1000000007
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

vector<int> a[N];
int f[N],c[N],b[N],d[N],ans[N],match[N],cnt;

bool dfs(int x)
{
	f[x]=cnt;
	for (auto i:a[x])
	{
		if (match[i]!=-1 & f[match[i]]==cnt) continue;
		if (match[i]==-1 || dfs(match[i]))
		{
			match[i]=x;
			return 1;
		}
	}
	return 0;
}

int main()
{
	int n,m;
	scc(n,m);
	for (int i=1;i<=n;i++) sc(c[i]);
	for (int i=1;i<=n;i++) sc(b[i]);
	int q;
	sc(q);
	for (int i=1;i<=q;i++) sc(d[i]),f[d[i]]=1;
	for (int i=1;i<=n;i++) if (!f[i])
		a[c[i]].pb(b[i]);
	memset(match,-1,sizeof match);
	cnt=1;
	for (int i=q;i;i--)
	{
		cnt++;
		ans[i]=ans[i+1];
		while(dfs(ans[i])) {ans[i]++;cnt++;}
		a[c[d[i]]].pb(b[d[i]]);
	}
	for (int i=1;i<=q;i++)printf("%d\n",ans[i]);
}