「BZOJ3438」小M的作物(最小割
3438: 小M的作物
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1891 Solved: 801
[Submit][Status][Discuss]
Description
小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子
有1个(就是可以种一棵作物)(用1...n编号),现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植
可以获得bi的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益
,小M找到了规则中共有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以
获得c2i的额外收益,所以,小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?
Input
第一行包括一个整数n
第二行包括n个整数,表示ai第三行包括n个整数,表示bi第四行包括一个整数m接下来m行,
对于接下来的第i行:第一个整数ki,表示第i个作物组合中共有ki种作物,
接下来两个整数c1i,c2i,接下来ki个整数,表示该组合中的作物编号。输出格式
Output
只有一行,包括一个整数,表示最大收益
Sample Input
3
4 2 1
2 3 2
1
2 3 2 1 2
4 2 1
2 3 2
1
2 3 2 1 2
Sample Output
11
样例解释A耕地种1,2,B耕地种3,收益4+2+3+2=11。
1<=k< n<= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。
样例解释A耕地种1,2,B耕地种3,收益4+2+3+2=11。
1<=k< n<= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。
题解
被洛谷大佬刷到绿了 好过分QAQ
读了题之后就在想怎么处理组合和个体之间的不相容性。一开始打算用拆点的方法限流,然后粗略算了一下 边数会爆炸QAQ
然后看了题解(=_=),fa现可以巧妙的用上最小割
把源点当作A,汇点当作B,源点往每个作物连ai,作物往B连bi。
首先对于没有组合:
割一下,留在左边的种进A,右边的种进B,那么收益就是剩下来的边权之和。
对于组合:
建一个虚点,从源点连一条c1i,再往每个受它控制的点连INF;
再建一个虚点,往汇点连一条c2i,再从每个受它控制的点连INF;
(图是毛来的)
其中黄边为对应值,蓝边为INF
以下为建图合理性的证明:
首先,INF永远割不掉。
对于左边,如果S->c已经被割掉,也就是说c变成了B的大宝贝儿,
想让c这条支流断掉,有两种选择:割左边的黄线,或者把右边的黄线和c->T一起割掉。
而如果割右边的,则不符合一开始让c加入B战队的假定,且S->c割的不明不白。
所以反证得知此时左边的黄线肯定会断。
同理可证,如果c->T被割掉,X'到T也会断。
∴证得割掉组合内任意点,这个组合的群体优惠也会断掉。
而如果某个组合的左边黄线存在,
那么割它下面的绿线都是白割,反正过的去。
对于右边同理。
∴如果某个组合优惠被得到了,那么它的所有儿砸都会留下。易知此时流量既得到了组合优惠,也收到了单个收益。
所以这样建图是ojbk的。
建了图然后跑个dinic算最小割,再用总边权减一下就好了。
撒花~
#include<cmath> #include<queue> #include<cstdio> #include<cstring> #include<iostream> using namespace std; const int INF=2*1e9+1; struct emm{ int e,f,v; }a[2000007]; int h[50007]; int tot=1; int s,t; void con(int x,int y,int w) { a[++tot].f=h[x]; h[x]=tot; a[tot].e=y; a[tot].v=w; a[++tot].f=h[y]; h[y]=tot; a[tot].e=x; return; } queue<int>q; int d[50007]; inline bool bfs() { memset(d,0,sizeof(d)); d[s]=1; q.push(s); while(!q.empty()) { int x=q.front();q.pop(); for(int i=h[x];i;i=a[i].f) if(!d[a[i].e]&&a[i].v) { d[a[i].e]=d[x]+1; q.push(a[i].e); } } return d[t]; } int dfs(int x,int al) { if(x==t||!al)return al; int fl=0; for(int i=h[x];i;i=a[i].f) if(d[a[i].e]==d[x]+1&&a[i].v) { int f=dfs(a[i].e,min(a[i].v,al)); if(f) { fl+=f; al-=f; a[i].v-=f; a[i^1].v+=f; if(!al)break; } } if(!fl)d[x]=-1; return fl; } int main() { //freopen("a.in","r",stdin); int n; scanf("%d",&n); s=0,t=50001; long long all=0; for(int i=1;i<=n;++i) { int x; scanf("%d",&x); all+=x; con(s,i,x); } for(int i=1;i<=n;++i) { int x; scanf("%d",&x); all+=x; con(i,t,x); } int m; scanf("%d",&m); int nod=n; for(int i=1;i<=m;++i) { int ki,c1,c2; scanf("%d%d%d",&ki,&c1,&c2); nod++; con(s,nod,c1); con(nod+1,t,c2); all+=c1; all+=c2; for(int j=1;j<=ki;++j) { int x; scanf("%d",&x); con(nod,x,INF); con(x,nod+1,INF); } nod++; } long long ans=0; while(bfs()){ans+=dfs(s,INF);} cout<<all-ans; return 0; }