题目描述

小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1...n编号)。

现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益,小M找到了规则***有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以获得c2i的额外收益。

小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?

输入格式

第一行包括一个整数n

第二行包括n个整数,表示ai第三行包括n个整数,表示bi第四行包括一个整数m接下来m行,

对于接下来的第i行:第一个整数ki,表示第i个作物组合***有ki种作物,

接下来两个整数c1i,c2i,接下来ki个整数,表示该组合中的作物编号。

输出格式

只有一行,包括一个整数,表示最大收益

输入输出样例

输入 #1<button class="copy&#45;btn lfe&#45;form&#45;sz&#45;middle" data&#45;v&#45;370e72e2="" data&#45;v&#45;52f2d52f="" type="button">复制</button>
3
4 2 1
2 3 2
1
2 3 2 1 2
输出 #1<button class="copy&#45;btn lfe&#45;form&#45;sz&#45;middle" data&#45;v&#45;370e72e2="" data&#45;v&#45;52f2d52f="" type="button">复制</button>
11

说明/提示

样例解释

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

数据范围与约定

1<=k< n<= 1000,0 < m < = 1000 保证所有数据及结果不超过2*10^9。

 

思路

  如果把农作物作为中间点,农场 A 设为源, B 设为汇。

  因为对于每个农作物只能种在一个农场中,所以该题可以等效为一个最小割模型。

  所以如何对 bonus 的组合建边是关键。

  把整个组合点集看作是一个点并拆成出入两点,由A有一条到入点的路,且从出点有一条到B的路,再逐次将待加入的点添加进点集就可以了。

  这样能获得的最大值就是总价值 - 最小割

 

CODE

 

  

#include < cstdio >
#include < iostream >
#include < cstring >
#include < queue >
#include < vector >
#include < algorithm >

using  namespace std ;

const  int maxn  =  1 e 6  +  7 ;
const  int inf  =  0x 3f3f3f3f ;

template < class T > inline  void  read(& res )
{
     char c ;T flag = 1 ;
     while((c = getchar()) < ' 0 ' ||c > ' 9 ' )if(c == ' - ' )flag =- 1 ;res =c - ' 0 ' ;
     while((c = getchar()) >= ' 0 ' &&c <= ' 9 ' )res =res * 10 +c - ' 0 ' ;res *=flag ;
}

struct edge { int from ,to ,cap ,flow ;};

struct isap
{
     int n ,s ,t , p [maxn ], d [maxn ], cur [maxn ], num [maxn ];
     bool  vis [maxn ];
    vector <int> g [maxn ];
    vector <edge >edges ;
     void  init( int  n , int  s , int  t )  {
         this -> n  = n ;
         this -> s  = s ;
         this -> t  = t ;
         for( int i  =  1 ;<= n ;i ++ )  g [i ].clear();
         edges .clear();
     }
     void  addegde( int  from , int  to , int  cap )  {
         edges .push_back((edge ){from , to , cap ,  0} );
         edges .push_back((edge ){to , from ,  0 ,  0} );
         int m  =  edges .size();
         g [from ].push_back(m - 2 );
         g [to ].push_back(m - 1 );
     }

     int  augment()  { ///找增广路
         int x  = t ,= inf ;
         while(x !=s )  {
            a  =  min(a ,  edges [ p [x ]]. cap  -  edges [ p [x ]]. flow );
            x  =  edges [ p [x ]]. from ;
         }
        x =t ;
         while(!= s )  {
             edges [ p [x ]]. flow  += a ;
             edges [ p [x ] ^ 1 ]. flow  =  -a ;
            x  =  edges [ p [x ]]. from ;
         }
         return a ;
     }
     int  maxflow()  { ///更新最大流
         int flow  =  0 ;
         memset(num ,  0 ,  sizeof (num ));
         memset(cur ,  0 ,  sizeof (cur ));
         for( int i  =  1 ; i  <= n ; i ++ )  num [ d [i ]] ++ ;
         int x  = s ;
         while( d [s ]  < n )  { ///最长的一条链上,最大的下标是nv-1,如果大于等于nv说明已断层
             if(== t )  {
                flow  +=  augment();
                x  = s ; //回退
             }
             bool ok  =  0 ;
             for( int i  =  cur [x ]; i  <  g [x ].size(); i ++ )  {
                edge  &=  edges [ g [x ][i ]];
                 if( d [x ]  ==  d [ e . to ]  +  1  &&  e . cap  >  e . flow )  {
                     p [ e . to ]  =  g [x ][i ];
                     cur [x ]  = i ;=  e . to ;
                    ok  =  1 ;
                     break;
                 }
             }
             if( !ok )  {
                 int m  = n - 1 ;
                 for( int i  =  0 ; i  <  g [x ].size();i ++ )  {
                    edge  &e = edges [ g [x ][i ]];
                     if( e . cap > e . flow ) m = min(m , d [ e . to ]);
                 }
                 num [ d [x ]] -- ;
                 if( ! num [ d [x ]])  break;
                 d [x ]  = m + 1 ;
                 num [ d [x ]] ++ ;
                 cur [x ]  =  0 ;
                 if(!= s ) x  =  edges [ p [x ]]. from ;
             }
         }
         return flow ;
     }
}ISAP ;

int n , p , q ;
int s , t ;
int tot  =  0 ;
int m ;

int  main()
{
     freopen( " data.txt " ,  " r " , stdin );
     read(n );
    s  = n  +  1 , t  = s  +  1 ;
     ISAP .init(* n , s , t );
     for  (  int i  =  1 ; i  <= n ;  ++)  {
         int x ;  read(x );
        tot  += x ;
         ISAP .addegde(s , i , x );
     }
     for  (  int i  =  1 ; i  <= n ;  ++)  {
         int x ;  read(x );
        tot  += x ;
         ISAP .addegde(i , t , x );
     }   
     read(m );
     for  (  int i  =  1 ; i  <= m ;  ++)  {
         int k ;
         read(k );
         int suma  =  0 , sumb  =  0 ;
         read(suma );  read(sumb );
        tot  += suma  + sumb ;
         ISAP .addegde(s , n  + i  +  2 , suma );
         ISAP .addegde(+ i  + m  +  2 , t , sumb );
         for  (  int j  =  1 ; j  <= k ;  ++)  {
             int x ;
             read(x );
             ISAP .addegde(+  2  + i , x , inf );
             ISAP .addegde(x , n  + i  + m  +  2 , inf );
         }
     }
    //cout << tot << " ! " << ISAP.maxflow() << endl;
     int mincut  =  ISAP .maxflow();
     printf( " %d \n " ,tot  - mincut );
     return  0 ;
}