LOJ6045 价
价
人类智慧之神zhangzj最近有点胖,所以要减肥,他买了\(N\)种减肥药,发现每种减肥药使用了若干种药材,总共正好有\(N\)种不同的药材。
经过他的人脑实验,他发现如果他吃下去了\(K~(0\leq K\leq N)\)种减肥药,而这\(K\)种减肥药使用的药材并集大小也为\(K\),这\(K\)种才会有效果,否则无效。
第\(i\)种减肥药在产生效果的时候会使zhangzj的体重增加\(P_i\)斤,显然\(P_i\)可以小于\(0\)。
他想知道,一次吃药最好情况下体重变化量是多少,当然可以一种药也不吃,此时体重不变。
由于某些奥妙重重的情况,我们可以让这\(N\)种减肥药每一种对应一个其使用的药材,且\(N\)种减肥药对应的药材互不相同(即有完美匹配)。
对于\(100\%\)的数据,\(1 \leq N \leq 300,~|P_i| \leq 1000000\)。
题解
把减肥药的权值设成\(-P_i+\inf\),把药材的权值设成\(-\inf\),然后看成最大权闭合子点集中间连\(\infty\)的边,跑个最小割就能得出答案了。
会不会出现选了\(x\)种减肥药,并且对应了\(y~(y>x)\)种药材的情况呢?如果出现了的话,因为\(y\times\inf>x\times(-P_i+\infty)\),所以左侧一定割断了至少一条边。对应最大权闭合子点集的意义,那么左侧一定有至少一种减肥药没有被选,与假设矛盾。所以不存在这种情况,正确性有保证。
CO int64 inf=1e18,mx=1e9;
CO int N=1e3;
namespace Flow{
int S,T;
struct edge {int v;int64 c;int a;};
vector<edge> to[N];
int dis[N];
IN void init(int n){
S=n-1,T=n;
}
IN void link(int u,int v,int64 c){
to[u].push_back({v,c}),to[v].push_back({u,0});
to[u].back().a=to[v].size()-1,to[v].back().a=to[u].size()-1;
}
bool bfs(){
fill(dis+1,dis+T+1,-1),dis[T]=0;
deque<int> Q={T};
while(Q.size()){
int u=Q.front();Q.pop_front();
for(CO edge&e:to[u])
if(to[e.v][e.a].c and dis[e.v]==-1){
dis[e.v]=dis[u]+1;
Q.push_back(e.v);
}
}
return dis[S]!=-1;
}
int64 dfs(int u,int64 lim){
if(u==T) return lim;
int64 rest=lim;
for(edge&e:to[u])
if(e.c and dis[e.v]==dis[u]-1){
int delta=dfs(e.v,min(e.c,rest));
if(!delta) {dis[e.v]=-1; continue;}
rest-=delta,e.c-=delta,to[e.v][e.a].c+=delta;
if(!rest) break;
}
return lim-rest;
}
int64 main(){
int64 ans=0;
while(bfs()) ans+=dfs(S,inf);
return ans;
}
}
int main(){
int n=read<int>();
Flow::init(2*n+2);
for(int i=1;i<=n;++i)for(int t=read<int>();t--;)
Flow::link(i,n+read<int>(),inf);
int64 ans=0;
for(int i=1;i<=n;++i){
int64 p=read<int64>();
Flow::link(Flow::S,i,mx-p);
ans+=mx-p;
Flow::link(n+i,Flow::T,mx);
}
ans-=Flow::main();
printf("%lld\n",-ans);
return 0;
}
静渊以有谋,疏通而知事。