前言
我是傻逼
正文
题意:
有 n n n个学生和 m m m个社团,第 i i i个学生能力值为 p i p_i pi,属于社团 c i c_i ci。
在
d
d
d天内 每天从每个社团里选出一个人组成一队
S
S
S
该队的能力值为
m
e
x
(
S
)
mex(S)
mex(S)
第 i i i天时第 k i k_i ki个学生会离开社团(在该天组队之前)
求每天能选出的队伍能力值最大是多少
1
≤
m
≤
n
≤
5000
1\le m \le n \le 5000
1≤m≤n≤5000
0
≤
p
i
<
5000
0 \le p_i < 5000
0≤pi<5000
1
≤
c
i
≤
m
1 \le c_i \le m
1≤ci≤m
1
≤
d
≤
n
1 \le d \le n
1≤d≤n
1
≤
k
i
≤
n
1 \le k_i \le n
1≤ki≤n
每个社团每天最多选出一个学生,仅当社团为空时选不出学生。
由于要求mex,匹配数和答案就没什么关系了,只需要看看从0开始出现的第一个找不到匹配的数是哪个就行。
而且每个能力值只派出一个学生的答案是不会比每个能力值派出多个学生的答案差的。
考虑将学生能力值向社团连边,跑匹配。每次询问相当于给能力值从小到大找匹配
考虑时间逆序 相当于每次往社团中加人 那么在二分图中多加一条边即可,然后每次询问从上一次的答案开始处理。
#include<bits/stdc++.h>
using namespace std;
#define mk make_pair
const int maxn = 10007;
int n, m, d, p[maxn], k[maxn], c[maxn], ans[maxn], vis[maxn], match[maxn];
vector<int> adj[maxn];
bool dfs(int u) {
for (int i = 0; i < adj[u].size(); i++) {
int v = adj[u][i];
if (!vis[v]) {
vis[v] = 1;
if (match[v] == -1 || dfs(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> p[i];
for (int i = 1; i <= n; i++) {
cin >> c[i];
}
cin >> d;
for (int i = 1; i <= d; i++) {
cin >> k[i];
vis[k[i]] = 1;
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
adj[p[i]].push_back(c[i] + 5000);
adj[c[i] + 5000].push_back(p[i]);
}
}
memset(match, -1, sizeof(match));
memset(vis, 0, sizeof(vis));
while (dfs(ans[d])) {
ans[d]++;
memset(vis, 0, sizeof(vis));
}
for (int i = d - 1; i; i--) {
adj[p[k[i+1]]].push_back(c[k[i+1]] + 5000);
adj[c[k[i+1]] + 5000].push_back(p[k[i+1]]);
ans[i] = ans[i+1];
memset(vis, 0, sizeof(vis));
while (dfs(ans[i])) {
ans[i]++;
memset(vis, 0, sizeof(vis));
}
}
for (int i = 1; i <= d; i++) printf("%d\n", ans[i]);
}
总结
最大化mex(S)时 考虑让S从0开始取
考虑时间的逆序操作