2020牛客多校一 H.Minimum-cost Flow

https://ac.nowcoder.com/acm/contest/5666/H
题意:给一个带费用网络,q次询问,每次询问限定每条边的容量都为 u / v u/v u/v,问把单位1流量从1运输到n的最小费用。
题解:边容量 u / v u/v u/v,运输1,等价于边容量 u / v ∗ ( v / u ) = 1 u/v*(v/u)=1 u/v(v/u)=1,运输 1 ∗ ( v / u ) = v / u 1*(v/u)=v/u 1(v/u)=v/u,最后费用乘以 u / v u/v u/v即可。
比赛时想每条边容量一样,没必要网络流,直接最短路,依次求最短路然后删掉路径,直到不能连通 1 − n 1-n 1n,这样做是有问题的。比如如图,如果第一次最短路求了中间那条,后面就错了,但是用网络流有反向弧,还可以回退。
贪心,优先走费用小的路径,每次增广保存下来就行了,最多就增广 m m m次。
2020牛客多校一 H.Minimum-cost Flow_第1张图片
代码:

#include
using namespace std;
#define maxn 55
#define INF 0x3f3f3f3f
#define P pair
#define MP(a,b) make_pair(a,b)
#define ll long long

int n,m,q,tot;
ll A,B;
ll sum[maxn];

struct Edge{
	int from,to,cap,flow,cost;
};

struct MCMF{
	int n,m,s,t;
	vector<Edge> edges;
	vector<int> G[maxn];
	int inq[maxn];
	ll d[maxn];
	int p[maxn];
	int a[maxn];

	void init()
	{
		edges.clear();
		for(int i=1;i<=n;i++)G[i].clear();
		int x,a,b,c,m0;
		s=1,t=n;
		cin>>m0;
		while(m0--)
		{
			cin>>a>>b>>c;
			AddEdge(a,b,1,c);
		}
	}

	void AddEdge(int from,int to,int cap,int cost)
	{
		edges.push_back((Edge){from,to,cap,0,cost});
		edges.push_back((Edge){to,from,0,0,-cost});
		m=edges.size();
		G[from].push_back(m-2);
		G[to].push_back(m-1);
	}

	bool BellmanFord(ll& flow,ll& cost)
	{
		for(int i=s;i<=t;i++)d[i]=INF;/***切记这里要改***/
		memset(inq,0,sizeof(inq));
		d[s]=0;inq[s]=1;p[s]=0;a[s]=INF;

		queue<int> Q;
		Q.push(s);
		while(!Q.empty())
		{
			int u=Q.front();Q.pop();
			inq[u]=0;
			for(int i=0;i<G[u].size();i++)
			{
				Edge& e=edges[G[u][i]];
				if(e.cap>e.flow && d[e.to]>d[u]+e.cost)
				{
					d[e.to]=d[u]+e.cost;
					p[e.to]=G[u][i];
					a[e.to]=min(a[u],e.cap-e.flow);
					if(inq[e.to]==0){inq[e.to]=1;Q.push(e.to);}
				}
			}
		}
		if(d[t]==INF)return false;
		flow+=a[t];
		cost+=a[t]*d[t];
		int u=t;
		while(u!=s)
		{
			edges[p[u]].flow+=a[t];
			edges[p[u]^1].flow-=a[t];
			u=edges[p[u]].from;
		}
		return true;
	}

	ll Mincost()
	{
		ll flow=0,cost=0;
		while(BellmanFord(flow,cost))sum[flow]=cost,tot++;
		return cost;
	}
}ans;

int main()
{
	//freopen("input.in","r",stdin);
	while(cin>>n)
	{
		ans.n=n;
		tot=0;
		ans.init();
		ans.Mincost();
        cin>>q;
        while(q--)
        {	
            scanf("%lld%lld",&A,&B);
            if(B>tot*A){puts("NaN");continue;}
            ll qian=B/A;
			if(qian*A>B)qian--;
			if((qian+1)*A<=B)qian++;
            ll mu=sum[qian]*A+(qian==tot?0:(sum[qian+1]-sum[qian])*(B-qian*A));
            ll g=__gcd(mu,B);
            printf("%lld/%lld\n",mu/g,B/g);
        }
	}
	return 0;
}

你可能感兴趣的:(网络流)