【最大权闭合子图/最小割】BZOJ3438-小M的作物【待填】

时间:2021-06-04 01:56:35

【题目大意】

小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1...n编号),现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以获得c2i的额外收益,所以,小M很快地算出了种植的最大收益。
 
【思路】
首先,如果没有组合方案应该怎么做呢?其实非常方便。
首先建立超级源点S和超级汇点T,S向每个i连一条容量为ai的边,T向每个i连一条容量为bi的边。显然答案=总的容量之和-最小割。
那么如果有了组合方案呢?
对于每一个方案,我们可以拆为两个点u和v,由S向u连一条容量为c1i的边;由v向T连一条容量为c2i的边。然后由S向组合里的每一个点连一条容量为INF的边,由组合里的每一个点向T连一条容量为INF的边。显然割边必定会是和与S或者T相连接的点。
可能这里会有一个疑惑:如果一个集合中的点的割边在和S相连的点中,而u,v的割边在和T相连的点中(也就是说我们把农作物种在了A中,却算了收益在B中。)
事实上是——不可能的。我们可以证明:u、v和它们组合中的割边一定同时和S或者T连,否则必定有一条S到T的通路,就无法形成割了!
所以最后答案=总的容量之和-最小割
画画图就明白了:)
 
...T了,不知道为什么。
删掉了STL部分。还是T了。我要静静。
/*还是TLE,回头看*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define S 0
#define T MAXN-1
using namespace std;
const int INF=0x7fffffff;
const int MAXN=+;
const int MAXM=+;
struct node
{
int fr,to,pos,cap;
};
int n,m,sum;
int dis[MAXN];
int vis[MAXN];
int cur[MAXN];
int first[MAXN],next[MAXM];
node edge[MAXM];
int tot=; int read() {
int x = ;
char ch = getchar();
while (ch < '' || '' < ch)
ch = getchar();
while ('' <= ch && ch <= '') {
x = x * + ch - '';
ch = getchar();
}
return x;
} void addedge(int u,int v,int c)
{
edge[++tot]=(node){u,v,tot+,c};
next[tot]=first[u];first[u]=tot;
edge[++tot]=(node){u,v,tot-,};
next[tot]=first[v];first[v]=tot;
} void build()
{
memset(first,-,sizeof(first));
memset(next,-,sizeof(next));
int ai,bi;
n=read();
for (int i=;i<=n;i++)
{
ai=read();
sum+=ai;
addedge(S,i,ai);
}
for (int i=;i<=n;i++)
{
bi=read();
sum+=bi;
addedge(i,T,bi);
}
m=read();
int k,c1i,c2i;
for (int i=;i<=m;i++)
{
k=read();c1i=read();c2i=read();
int u=n+i;
int v=n+i+m;
addedge(S,u,c1i);
addedge(v,T,c2i);
sum+=c1i+c2i;
for (int j=;j<=k;j++)
{
int ci;
ci=read();
addedge(u,ci,INF);
addedge(ci,v,INF);
}
}
} int bfs()
{
memset(dis,-,sizeof(dis));
int l=,r=;
int que[MAXN];
que[++r]=S;
dis[S]=; while (l<r)
{
int head=que[l];l++;
for (int i=first[head];i!=-;i=next[i])
{
node &tmp=edge[i];
if (dis[tmp.to]==- && tmp.cap>)
{
dis[tmp.to]=dis[head]+;
que[++r]=tmp.to;
}
}
}
return (dis[T]!=-);
} int dfs(int s,int t,int f)
{
vis[s]=;
if (s==t || !f) return f;
int res=;
for (int i=first[s];i!=-;i=next[i])
{
node &tmp=edge[i];
if (!vis[tmp.to] && tmp.cap> && dis[tmp.to]==dis[s]+)
{
int delta=dfs(tmp.to,t,min(tmp.cap,f));
if (delta>)
{
tmp.cap-=delta;
edge[tmp.pos].cap+=delta;
f-=delta;
res+=delta;
if (!f) return res;
}
}
}
return res;
} int dinic()
{
int flow=;
while (bfs())
{
memset(vis,,sizeof(vis));
flow+=dfs(S,T,INF);
}
return flow;
} int main()
{
build();
printf("%d\n",sum-dinic());
return ;
}