题目链接:点击打开链接
思路:
显然就是问最大权闭合图 和 能取最多的正点权个数
1、首先对于正权值的付出,直接取,而对于梦想也忽略正权值的付出,这样就转成一个裸的最大权闭合图了。
2、计算此时的正点权个数:把所有点权*大数C,然后把正点权值+1,跑出来流量就是 flow / C, 最多的正点权个数就是 正点权点集-flow%C.
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
//点标 [0,n]
typedef long long ll;
const int N = 3010;
const int M = 500010;
const ll INF = 1e18;
template<class T>
struct Max_Flow {
int n;
int Q[N], sign;
int head[N], level[N], cur[N], pre[N];
int nxt[M], pnt[M], E;
T cap[M];
void Init(int n) {
this->n = n+1;
E = 0;
std::fill(head, head + this->n, -1);
}
//有向rw 就= 0
void add(int from, int to, T c, T rw) {
pnt[E] = to;
cap[E] = c;
nxt[E] = head[from];
head[from] = E++;
pnt[E] = from;
cap[E] = rw;
nxt[E] = head[to];
head[to] = E++;
}
bool Bfs(int s, int t) {
sign = t;
std::fill(level, level + n, -1);
int *front = Q, *tail = Q;
*tail++ = t; level[t] = 0;
while(front < tail && level[s] == -1) {
int u = *front++;
for(int e = head[u]; e != -1; e = nxt[e]) {
if(cap[e ^ 1] > 0 && level[pnt[e]] < 0) {
level[pnt[e]] = level[u] + 1;
*tail ++ = pnt[e];
}
}
}
return level[s] != -1;
}
void Push(int t, T &flow) {
T mi = INF;
int p = pre[t];
for(int p = pre[t]; p != -1; p = pre[pnt[p ^ 1]]) {
mi = std::min(mi, cap[p]);
}
for(int p = pre[t]; p != -1; p = pre[pnt[p ^ 1]]) {
cap[p] -= mi;
if(!cap[p]) {
sign = pnt[p ^ 1];
}
cap[p ^ 1] += mi;
}
flow += mi;
}
void Dfs(int u, int t, T &flow) {
if(u == t) {
Push(t, flow);
return ;
}
for(int &e = cur[u]; e != -1; e = nxt[e]) {
if(cap[e] > 0 && level[u] - 1 == level[pnt[e]]) {
pre[pnt[e]] = e;
Dfs(pnt[e], t, flow);
if(level[sign] > level[u]) {
return ;
}
sign = t;
}
}
}
T Dinic(int s, int t) {
pre[s] = -1;
T flow = 0;
while(Bfs(s, t)) {
std::copy(head, head + n, cur);
Dfs(s, t, flow);
}
return flow;
}
};
Max_Flow <ll>F;
ll dream[N], work[N], ans;
int from, to;
int n, m;
const ll C = 1e6;
void input(){
ans = 0;
from = 0; to = n+m+1;
F.Init(to);
for(int i = 1; i <= n; i++)
{
scanf("%lld", &dream[i]);
F.add(from, i, dream[i]*C+1LL, 0);
ans += dream[i];
}
for(int i = 1; i <= m; i++)
{
scanf("%lld", &work[i]);
if(work[i] >= 0) ans += work[i];
else
F.add(n +i, to, -work[i]*C, 0);
}
for(int i = 1, siz, u; i <= n; i++)
{
scanf("%d", &siz);
while(siz--){
scanf("%d", &u);
if(work[u] >= 0)continue;
F.add(i, n+u, INF, 0);
}
}
}
int main() {
while(~scanf("%d %d", &n, &m)){
input();
ll flow = F.Dinic(from,to);
// cout<<"FLOW:"<<flow<<endl;
cout<< ans - flow / C << " " << n-flow % C <<endl;
}
return 0;
}
/*
3 3
2 3 5
-2 -3 -5
2 1 2
2 1 2
1 3
3 3
0 0 0
-1 -10 -10
2 1 2
2 1 2
3 3 2 1
*/