对于测试点$2,3$,由于数据很小,瞎暴力都能过,于是就获得了$70$高分。
对于全部的数据,显然是贪心的从第一人到第$n$人计算,对于第$i$个人,按照他的志愿从$1$到$m$加边,每次加一级志愿的边,然后在网络上找增广路,如果找到了增广路,就可以退出了,至此完美解决了第一问。
第一问可以优化,如果第$i$个人的第$j$级志愿不能增广,就直接把这些边删掉。
第二问只需要对于每一个人二分答案$ans_i$,把$ans_i$之前的人全部按照最优方案建出图,然后额外的连上第$i$个人所期望的边,再寻找增广路,就可以完美解决第二问。
上面的算法可以看出来要很多次建图,又因为图最多只有$2n$的点和$nC$的边,那就可以暴力的存$n$个图,分别对应前$n$个人的最优方案的残余网络,这样可以大大减少建图浪费的时间,并且每次只需要单路增广。
用$vector$貌似会快一些?
复杂度O(不慢),第一问最多$nm$次找增广路,第二问$nlogn$次,及时删掉没用的边就不会$TLE$。
```cpp
#include <queue>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int read()
{
int x = 0, f = 1; char ch = getchar();
while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int MAX = 233;
const int MAXN = 466;
const int inf = 100000000;
int n, m, s, t;
int b[MAX], p[MAX], ans[MAX], ans2[MAX];
vector<int> a[MAX][MAX];
struct edge
{
int to, cap, rev;
edge() {}
edge(int a, int b, int c)
{
to = a, cap = b, rev = c;
}
};
struct Graph
{
vector<edge> e[MAXN];
queue<int> que;
int dis[MAXN];
void addedge(int u, int v, int cap)
{
e[u].push_back(edge(v, cap, e[v].size()));
e[v].push_back(edge(u, 0, e[u].size() - 1));
}
void deledge(int x) { e[x].pop_back(); }
bool bfs()
{
memset(dis, -1, sizeof dis);
que.push(s);
dis[s] = 0;
while (!que.empty())
{
int u = que.front();
que.pop();
for (vector<edge>::iterator i = e[u].begin(); i != e[u].end(); i++)
if (i -> cap && dis[i -> to] < 0)
{
dis[i -> to] = dis[u] + 1;
que.push(i -> to);
}
}
return dis[t] != -1;
}
int dfs(int u, int f)
{
if (u == t) return f;
for (vector<edge>::iterator i = e[u].begin(); i != e[u].end(); i++)
if (i -> cap && dis[i -> to] > dis[u])
{
int d = dfs(i -> to, min(f, i -> cap));
if (d)
{
i -> cap -= d;
e[i -> to][i -> rev].cap += d;
return d;
}
}
return 0;
}
void clear()
{
for (int i = 1; i <= n + m + 2; i++)
e[i].clear();
}
} G[233], tG;
void solveq1()
{
for (int i = 1; i <= m; i++)
G[0].addedge(i + n, t, b[i]);
for (int i = 1; i <= n; i++)
{
G[i] = G[i - 1];
G[i].addedge(s, i, 1);
for (int j = 1; j <= m; j++)
{
for (vector<int>::iterator k = a[i][j].begin(); k != a[i][j].end(); k++)
G[i].addedge(i, *k + n, 1);
if (G[i].bfs())
{
G[i].dfs(s, inf);
ans[i] = j;
break;
}
for (vector<int>::iterator k = a[i][j].begin(); k != a[i][j].end(); k++)
G[i].deledge(i), G[i].deledge(*k + n);
}
}
for (int i = 1; i <= n; i++)
{
if (!ans[i]) ans[i] = m + 1;
printf("%d ", ans[i]);
}
putchar('\n');
}
bool check(int u, int x)
{
tG = G[x - 1];
tG.addedge(s, u, 1);
for (int i = 1; i <= p[u]; i++)
for (vector<int>::iterator j = a[u][i].begin(); j != a[u][i].end(); j++)
tG.addedge(u, *j + n, 1);
if (tG.bfs()) return 1;
else return 0;
}
void solveq2()
{
for (int i = 1; i <= n; i++)
{
if (ans[i] <= p[i])
continue;
ans2[i] = 1000;
int l = 1, r = i - 1;
while (r >= l)
{
int mid = (l + r) >> 1;
if (check(i, mid))
ans2[i] = mid, l = mid + 1;
else r = mid - 1;
}
if (ans2[i] && ans2[i] != 1000)
ans2[i] = i - ans2[i];
}
for (int i = 1; i <= n; i++)
printf("%d ", ans2[i] == 1000 ? i : ans2[i]);
putchar('\n');
}
void init()
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
a[i][j].clear();
for (int i = 1; i <= n; i++)
ans[i] = ans2[i] = 0;
G[0].clear();
}
void solve()
{
n = read(), m = read();
init();
for (int i = 1; i <= m; i++)
b[i] = read();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
int x = read();
if (x) a[i][x].push_back(j);
}
for (int i = 1; i <= n; i++)
p[i] = read();
s = n + m + 1, t = s + 1;
solveq1();
solveq2();
}
int main()
{
int T = read(), C = read();
while (T--) solve();
return 0;
}
```