http://media.hihocoder.com/contests/icpcbeijing2015/problems.pdf
题意:
题意:给一颗有向树,技能获得的前提是他的前置技能都获得了,作为一个玩家,你有特权:
1.直接花费一定数量的钱获得某个技能。
2.花费一定数量的钱将一个技能的某一个前置关系取消,即将前置技能到该技能的边消除(不需要获得前置技能)。
如果正常学习技能的话每一个技能都要花费一定量的时间,问获得指定的技能的最少的花费是多少。
分析:
网络流
1.将所有点拆成i和i'两个点,在建好的图中就用i和i+n分别表示。
2.若i->j有边(i为j的前置技能),则将i'到j建边,权值为用钱将该边消除的花费。
3.将源点和i建边,边权为正常学习该技能花费的时间。
4.将i与i'建边,权值为用钱直接获得该点的花费。【求这一条边和(通过减掉前置学习该点的花费/学习前置中最小的+基础花费)的最小值·】
5.将S’与汇点建边,权值为inf。
这样最大流跑完了以后,S’与汇点之间正向边的流量就是源点与汇点的最小割,也是要求的最小花费。
代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define clear(A,X) memset(A,X,sizeof A)
#define copy(A,B) memcpy(A,B,sizeof A)
using namespace std;
const int maxe=1e6+10;
const int maxq=1e6+10;
const int maxn=1e5+10;
struct Edge{
int v;
int c;
int n;
}edge[maxe];
int adj[maxn],cnte;
int Q[maxq],head,tail;
int d[maxn],cur[maxn],pre[maxn],num[maxn];
int sourse,sink,nv;//sourse:源点,sink:汇点,nv:编号修改的上限
int n,m;
void add(int u,int v,int c)
{
edge[cnte].v=v;
edge[cnte].c=c;
edge[cnte].n=adj[u];
adj[u]=cnte++;
edge[cnte].v=u;
edge[cnte].c=0;
edge[cnte].n=adj[v];
adj[v]=cnte++;
}
void rev_bfs()
{
clear(num,0);
clear(d,-1);
d[sink]=0;
num[0]=1;
head=tail=0;
Q[tail++]=sink;
while(head!=tail)
{
int u=Q[head++];
for(int i=adj[u];~i;i=edge[i].n)
{
int v=edge[i].v;
if(~d[v]) continue;
d[v]=d[u]+1;
Q[tail++]=v;
num[d[v]]++;
}
}
}
int ISAP()
{
copy(cur,adj);
rev_bfs();
int flow=0,u=pre[sourse]=sourse,i;
while(d[sink]<nv)
{
if(u==sink)
{
int f=inf,neck;
for(i=sourse;i!=sink;i=edge[cur[i]].v)
{
if(f>edge[cur[i]].c)
{
f=edge[cur[i]].c;
neck=i;
}
}
for(i=sourse;i!=sink;i=edge[cur[i]].v)
{
edge[cur[i]].c-=f;
edge[cur[i]^1].c+=f;
}
flow+=f;
u=neck;
}
for(i=cur[u];~i;i=edge[i].n)
if(d[edge[i].v]+1==d[u]&&edge[i].c) break;
if(~i)
{
cur[u]=i;
pre[edge[i].v]=u;
u=edge[i].v;
}
else
{
if(0==(--num[d[u]])) break;
int mind=nv;
for(i=adj[u];~i;i=edge[i].n)
{
if(edge[i].c&&mind>d[edge[i].v])
{
cur[u]=i;
mind=d[edge[i].v];
}
}
d[u]=mind+1;
num[d[u]]++;
u=pre[u];
}
}
return flow;
}
void init()
{
clear(adj,-1);
cnte=0;
}
void work()
{
int S;
scanf("%d%d%d",&n,&m,&S);
int u,v,c;
init();
for(int i=0;i<m;++i)
scanf("%d%d%d",&u,&v,&c),add(u+n,v,c);
for(int i=1;i<=n;i++)
scanf("%d",&c),add(0,i,c);
for(int i=1;i<=n;i++)
scanf("%d",&c),add(i,i+n,c);
add(S+n,2*n+1,inf);
sourse=0;sink=2*n+1;nv=sink+1;
printf("%d\n",ISAP());
}
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--) work();
return 0;
}