jzoj 1287. 躲雨

Question

给你nn个点,mm条边。每个点都有一个容量限制和当前存在的量,每条边都有一个所需时间。
求当所有点的量都符合限制是的最少需要时间。

Solution

我们先用floydfloyd求出两两点之间的最短时间。
然后二分答案,对于时间<=mid<=mid的边就加,而后跑一遍网络流看看是否全都能流完即可。
加边具体操作:
设起始点为SS,终点为TT
1.SS向第ii个点连一条a[i]a[i]的边
2.第n+in+i个点向TT连一条b[i]b[i]的边
3.对于时间<=mid<=mid的原边(i,n+i)(i,n+i),第ii个点向第n+in+i个点连一条a[i]a[i]的边

Code

#include<cstdio>
#include<cstring>
#define N 210
#define ll long long
using namespace std;
struct node{int v,fr,flow;}e[N*N*20];
int n,m,a[N],b[N],tail[N<<1],cnt;
int S=0,T,num=0,dis[N<<1],gap[N<<1];
ll l=0,r=0,mid,ans=-1,fl[N][N];

inline int read()
{
	int x=0; char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

inline ll min(ll x,ll y) {return x<y ? x:y;}

inline void add(int u,int v,int flow)
{
	e[++cnt]=(node){v,tail[u],flow}; tail[u]=cnt;
	e[++cnt]=(node){u,tail[v],0}; tail[v]=cnt;
}

int dfs(int x,int hav)
{
	if (x==T) return hav;
	int gone=0;
	for (int p=tail[x],v,can;p;p=e[p].fr)
	{
		v=e[p].v;
		if (dis[v]+1!=dis[x] || !e[p].flow) continue;
		can=dfs(v,min(hav-gone,e[p].flow));
		e[p].flow-=can,e[p^1].flow+=can,gone+=can;
		if (gone==hav) return gone;
	}
	gap[dis[x]]--;
	if (!gap[dis[x]]) dis[S]=T;
	gap[++dis[x]]++;
	return gone;
}

bool check(ll mid)
{
	cnt=1;
	memset(tail,0,sizeof(tail));
	memset(dis,0,sizeof(dis));
	memset(gap,0,sizeof(gap));
	for (int i=1;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			if (fl[i][j]<=mid) 
				add(i,n+j,a[i]),add(j,n+i,a[j]);
	for (int i=1;i<=n;i++)
		add(S,i,a[i]),add(n+i,T,b[i]),add(i,n+i,a[i]);
	int s=0;
	while (dis[S]<T) s+=dfs(S,num);
	if (s==num) return 1;
	else return 0;
}

int main()
{
	freopen("rain.in","r",stdin);
	freopen("rain.out","w",stdout);
	n=read(),m=read();T=n+n+1;
	for (int i=1;i<=n;i++)
		a[i]=read(),b[i]=read(),num+=a[i];
	memset(fl,60,sizeof(fl));
	for (int i=1,u,v,lg;i<=m;i++)
	{
		u=read(),v=read(),lg=read();r+=lg;
		fl[v][u]=fl[u][v]=min(fl[u][v],lg);
	}
	for (int k=1;k<=n;k++)
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
				fl[j][i]=fl[i][j]=min(fl[i][j],fl[i][k]+fl[k][j]);
	while (l<=r)
	{
		mid=l+r>>1;
		if (check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	printf("%lld
",ans);
	return 0;
}
转载需注明出处。
原文地址:https://www.cnblogs.com/jz929/p/11817518.html