【题目链接】
https://www.luogu.com.cn/problem/CF1139E
【题目描述】
有n个学生,第i个学生在俱乐部c_i,并拥有一个能力值a_i。在接下来的d天中,每天会有一位学生离开他所在的俱乐部,并且不会再加入任何俱乐部。并且,每天学校负责人都会从每个俱乐部中选出一个学生来组成一支战队,战队的战斗力为该战队学生能力值的集合中最小的没有出现的非负整数,请求出每天的战力分别是多少。
n
≤
5000
,
a
i
≤
1
0
9
n≤5000,a_i≤10^9
n≤5000,ai≤109
【分析】
先不考虑d天离开的学生,学生能力值
a
i
a_i
ai和社团
c
i
c_i
ci分别看作点,连接
<
a
i
,
c
i
>
<a_i,c_i>
<ai,ci>,构成一张二分图。判断当前集合的
m
e
x
mex
mex相当于从能力值0开始从小到大依次判断是否匹配,若扫描到能力值
a
j
a_j
aj时发现不能匹配,则当前集合的
m
e
x
=
a
j
mex=a_j
mex=aj。
接下来考虑离开的学生,如果正序解决,一是删边操作不好完成,二是每次询问都要重新做一次最大匹配,时间复杂度太高。像这样的问题往往要考虑倒序完成,让删边变为加边。
因为每次查询的答案肯定是非严格递减,每次匹配是从上一轮的
m
e
x
mex
mex开始。总的时间复杂度
O
(
n
2
)
O(n^2)
O(n2)
【代码】
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5005;
int n,m,p[maxn],c[maxn],d,k[maxn];
bool stu[maxn];
void read()
{ memset(stu,1,sizeof(stu));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
scanf("%d",&d);
for(int i=1;i<=d;i++)
{ scanf("%d",&k[i]);
stu[k[i]] = 0;
}
}
struct E
{
int v,ne;
}edge[maxn];
int head[maxn],tot=1;
void add(int u,int v)
{
edge[++tot] = (E){v,head[u]};
head[u] = tot;
}
bool vis[maxn];
int match[maxn];
bool dfs(int x)
{ int y;
for(int i = head[x];i;i=edge[i].ne)
{
y = edge[i].v;
if(!vis[y])
{
vis[y] = 1;
if(match[y] == -1 || dfs(match[y]))
{
match[y] = x;
return true;
}
}
}
return false;
}
void work()
{
//build
for(int i=1;i<=n;i++)
{ match[i] = -1;
if(stu[i])
add(p[i],c[i]);
}
//ans
int ans[maxn],mex=0;
for(int i=d;i>=1;i--)
{
for(;mex<=n;mex++)
{ memset(vis,0,sizeof(vis));
if(!dfs(mex))
{
break;
}
}
ans[i] = mex;
add(p[k[i]],c[k[i]]);//第i天的学生归队
}
for(int i=1;i<=d;i++)
printf("%d\n",ans[i]);
}
int main()
{
read();
work();
return 0;
}