2163: 复杂的大门

Time Limit: 20 Sec Memory Limit: 259 MB
Submit: 490 Solved: 288
[Submit][Status][Discuss]
Description
你去找某bm玩,到了门口才发现要打开他家的大门不是一件容易的事……
他家的大门外有n个站台,用1到n的正整数编号。你需要对每个站台访问一定次数以后大门才能开启。站台之间有m个单向的传送门,通过传送门到达另一个站台不需要花费任何代价。而如果不通过传送门,你就需要乘坐公共汽车,并花费1单位的钱。值得庆幸的是,任意两个站台之间都有公共汽车直达。
现在给你每个站台必须访问的次数Fi,对于站台i,你必须恰好访问Fi次(不能超过)。
我们用u、v、w三个参数描述一个传送门,表示从站台u到站台v有一个最多可以使用w次的传送门(不一定要使用w次)。值得注意的是,对于任意一对传送门(u1,v1)和(u2,v2),如果有u1<u2,则有v1≤v2;如果有v1<v2,则有u1≤u2;且u1=u2和v1=v2不同时成立。
你可以从任意的站台开始,从任意的站台结束。出发去开始的站台需要花费1单位的钱。你需要求出打开大门最少需要花费多少单位的钱。
Input
第一行包含两个正整数n、m,意义见题目描述。第二行包含n个正整数,第i个数表示Fi。接下来有m行,每行有三个正整数u、v、w,表示从u到v有一个可以使用w次的传送门。

Output
输出一行一个整数,表示打开大门最少花费的钱数。

Sample Input
4 3

5 5 5 5

1 2 1

3 2 1

3 4 1
Sample Output
17

HINT
有20%的数据满足n≤10,m≤50;对于所有的w、Fi,满足1≤w,Fi≤10。有50%的数据满足n≤1000,m≤10000。100%的数据满足1≤n≤10000,1≤m≤100000;对于所有的u、v,满足1≤u,v≤n,u≠v;对于所有的w、Fi,满足1≤w,Fi≤50000。以上的每类数据中都存在50%的数据满足对于所有的w、Fi,有w=Fi=1。


比较直观的想法就是用最多的免费的,然后其他就会花费。所以对花费为0的跑最大流即可。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
const int N=2e4+10,M=1e6+10;
int n,m,s,t,h[N],res,val[N];
int head[N],nex[M],to[M],w[M],tot=1;
inline void ade(int a,int b,int c){
	to[++tot]=b; nex[tot]=head[a]; w[tot]=c; head[a]=tot;
}
inline void add(int a,int b,int c){
	ade(a,b,c);	ade(b,a,0);
}
inline int bfs(){
	queue<int> q;	q.push(s);	memset(h,0,sizeof h); h[s]=1;
	while(q.size()){
		int u=q.front();	q.pop();
		for(int i=head[u];i;i=nex[i]){
			if(w[i]&&!h[to[i]]){
				h[to[i]]=h[u]+1;	q.push(to[i]);
			}
		}
	}
	return h[t];
}
int dfs(int x,int f){
	if(x==t)	return f;	int fl=0;
	for(int i=head[x];i&&f;i=nex[i]){
		if(h[to[i]]==h[x]+1&&w[i]){
			int mi=dfs(to[i],min(w[i],f));
			w[i]-=mi; w[i^1]+=mi; fl+=mi; f-=mi;
		}
	}
	if(!fl)	h[x]=-1;
	return fl;
}
int dinic(){
	int res=0;
	while(bfs())	res+=dfs(s,inf);	
	return res; 
}
signed main(){
	cin>>n>>m; t=n*2+1; s=0;
	for(int i=1;i<=n;i++)	scanf("%d",&val[i]),res+=val[i];
	for(int i=1;i<=m;i++){
		int a,b,c;	scanf("%d %d %d",&a,&b,&c);	add(a,b+n,c);
	}
	for(int i=1;i<=n;i++)	add(i+n,t,val[i]),add(s,i,val[i]);
	printf("%d\n",res-dinic());
	return 0;
}