图片来自于 \(Hi\ Ho\)。
简化一下题面 :
- 一些人要拍照,拍照要一些 小 \(B\) 的下属一起拍,拍完了这些人会给小 \(B\) 钱。
- 给了一个下属的钱就可以让他拍很多次照片。
- 求小 \(B\) 的纯利润 (最大)。
很显然样例中可以全部人都拍,全部下属都给钱。 很显然这是一个最大权闭合子图问题。
先说一说什么是权闭合子图:
这里有的权闭合子图有 \((3).(4).(2,4).(3,4).(1,3,4).(2,3,4).(1,2,3,4)\)。我们在一个图中可以选一些点,形成一个集合 \(V\),但是要保证这些点所连 (有向边) 到的点也要在 \(V\) 里面。所以很显然 \((1)\) 不是权闭合子图因为 \(3,4\) 没有进入。
很显然这种图可以用二分图展示:
其中我们可以浅浅的理解,哪些点要哪些点。而如果我们建立了一个超汇的话,就可以知道 如果我们把让所有 \(B\)点连超汇,流量为所付的钱, \(A\)点连超源,流量为给的钱 ,那么我们就可以直接求 最大权闭合子图 了。因为我们流一条超源向 \(A\) 点的边就等于给他拍照拿他钱,而流 \(B\) 点向超汇的就是付给他钱 (而且仅仅一次,因为只流一次)。那么 \(A\) 向 \(B\) 边的流量就可以设置为 \(inf\)。
但是要知道结论: 最大权闭合子图的权值等于所有正权点之和减去最小割。(其中最小割等于最大流)
我们就跑一遍最大流就好啦。
(没想到连 _ 也可以当变量 \(qwq\))
// luogu-judger-enable-o2
Uses math;
Const _=100;
var
value,reach,next:array[-1..50000] of longint;
gap,dis,cnt:array[-1..50000] of longint;
i,l,r,n,m,source,sink,sum,tot,ans:longint;
maxflow:int64;
procedure add(l,r,sum:longint);
begin
inc(tot);
reach[tot]:=r;
value[tot]:=sum;
next[tot]:=cnt[l];
cnt[l]:=tot;
end;
function Dfs(now,flow:longint):longint;
var i,k,mindis,ret:longint;
begin
mindis:=n-1; ret:=flow;
if now=sink then exit(flow);
i:=cnt[now];
while i<>-1 do
begin
if value[i]>0 then
begin
if dis[now]=dis[reach[i]]+1 then
begin
k:=Dfs(reach[i],min(ret,value[i]));
dec(value[i],k); inc(value[i xor 1],k);
dec(ret,k);
if dis[source]>=n then exit(flow-ret);
if ret=0 then break;
end;
mindis:=min(mindis,dis[reach[i]]);
end;
i:=next[i];
end;
if ret=flow then
begin
dec(gap[dis[now]]);
if gap[dis[now]]=0 then dis[source]:=n;
dis[now]:=mindis+1;
inc(gap[dis[now]]);
end;
exit(flow-ret);
end;
begin
filldword(cnt,sizeof(cnt) div 4,maxlongint*2+1); tot:=1;
read(m,n); source:=0; sink:=n+m+1;
for i:=1 to m do
begin
read(l); inc(sum,l); add(source,i,l); add(i,source,0); read(l);
while l<>0 do begin add(i,l+_,maxlongint div 843); add(l+_,i,0); read(l); end;
end;
for i:=1 to n do begin read(l); add(i+_,sink,l); add(sink,i+_,0); end;
n:=n+m+_+1; gap[source]:=n;
while dis[source]<n do inc(maxflow,Dfs(source,maxlongint div 842));
writeln(sum-maxflow);
end.