【bzoj3438】【小M的作物】【最小割】

Description


 背景

    小M还是个特么喜欢玩MC的孩纸。。。

 描述

    小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
421
232
1
23212

Sample Output

11

样例解释

A耕地种1,2,B耕地种3,收益4+2+3+2=11。

数据范围与约定

对于100%的数据,1<=k< n<= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。
题解:
S表示种在耕地A上,T表示种在耕地B上.
对于每组作物新建两个点.分别表示同时选A和同时选B;
对于同时选A.
S向该点连同时选A的收益.
该点向它控制的点连正无穷的边.
这样可以保证它控制的点中只要有一个点选的B就要放弃同时选A的收益.
同时选B同理.
总收益-最小割即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 4000
#define M 2000000
#define inf 210000000
using namespace std;
int point[N],next[M<<1],n,m,x,sum,T,cnt(1),a[N],b[N],c1,c2,k;
int gap[N],cur[N],pre[N],dis[N];
bool f;
struct use{
  int st,en,v;
}e[M<<1];
void add(int x,int y,int v){
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=point[y];point[y]=cnt;
  e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;
}
int isap(){
  int i,mn,ans(0),u(1);
  gap[0]=T;
  for (i=1;i<=T;i++) cur[i]=point[i];
  while (dis[1]<T){
    f=false;
    for (i=cur[u];i;i=next[i])
      if (e[i].v&&dis[e[i].en]+1==dis[u]){cur[u]=i;f=true;break;}
    if (f){
      pre[u=e[i].en]=i;
      if (u==T){
        mn=inf;
        for (i=T;i!=1;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
        ans+=mn;
        for (i=T;i!=1;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn;
        u=1;
      }
    }
    else{
      gap[dis[u]]--;if (!gap[dis[u]]) return ans;
      for (mn=T,i=point[u];i;i=next[i]) 
        if (e[i].v) mn=min(mn,dis[e[i].en]);
      gap[dis[u]=mn+1]++;cur[u]=point[u];
      if (u!=1) u=e[pre[u]].st;
    }
  }
  return ans;
}
int main(){
  scanf("%d",&n);
  for (int i=1;i<=n;i++) scanf("%d",&a[i]);
  for (int i=1;i<=n;i++) scanf("%d",&b[i]);
  scanf("%d",&m);T=n+m+m+2;
  for (int i=1;i<=n;i++){
    sum+=a[i];
    add(1,i+1,a[i]);
  }
  for (int i=1;i<=n;i++){
    sum+=b[i];
    add(i+1,T,b[i]);
  }
  for (int i=1;i<=m;i++){
    scanf("%d%d%d",&k,&c1,&c2);
    add(1,(i-1)*2+1+n+1,c1);
    add(i*2+n+1,T,c2);
	sum+=c1+c2;
	for (int j=1;j<=k;j++){
      scanf("%d",&x);
	  add((i-1)*2+1+n+1,x+1,inf);
	  add(x+1,i*2+n+1,inf); 
    }
  }
  cout<<sum-isap();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值