(2020多校)H.Minimum-cost Flow(费用流的增广路)

传送门看这里吧

这题没有考察建图,而是考察费用流的本质。

比如每次spfa只会找一条增广路

比如越先找到的增广路花费一定越小


首 先 发 现 容 量 为 u / v 是 没 办 法 算 的 ( 分 数 ) 首先发现容量为u/v是没办法算的(分数) u/v()

边 容 量 u / v , 最 大 流 是 1 边容量u/v,最大流是1 u/v,1

转 化 为 边 容 量 u , 最 大 流 是 v ( 这 样 花 费 最 后 要 除 以 v ) 转化为边容量u,最大流是v(这样花费最后要除以v) u,v(v)

面 对 这 么 多 询 问 , 我 们 预 处 理 一 下 每 条 增 广 路 的 花 费 就 行 了 面对这么多询问,我们预处理一下每条增广路的花费就行了 ,广

按 题 目 建 图 , 流 量 暂 时 设 置 为 1 , 因 为 好 算 按题目建图,流量暂时设置为1,因为好算 ,1,

每 次 s p f a 后 , 到 t 的 流 量 一 定 是 1 , 所 以 只 需 要 记 录 每 条 增 广 路 的 花 费 在 p a t h 数 组 每次spfa后,到t的流量一定是1,所以只需要记录每条增广路的花费在path数组 spfa,t1,广path

那 么 现 在 每 个 询 问 里 边 容 量 是 u , 最 大 流 是 v 那么现在每个询问里边容量是u,最大流是v u,v

设 v = a u + b   ( b < u ) 设v=au+b\ (bv=au+b (b<u)

因 为 每 条 边 的 容 量 是 u , 所 以 至 少 需 要 a 条 增 广 路 因为每条边的容量是u,所以至少需要a条增广路 u,a广

如 果 b 不 为 0 , 那 么 第 a + 1 条 增 广 路 需 要 走 b u 如果b不为0,那么第a+1条增广路需要走\frac{b}{u} b0,a+1广ub

所 以 设 s u m n 是 p a t h 的 前 缀 和 数 组 所以设sumn是path的前缀和数组 sumnpath

花 费 是 s u m n [ x ] + p a t h [ x + 1 ] ∗ b / u 花费是sumn[x]+path[x+1]*b/u sumn[x]+path[x+1]b/u

但是不要忘记sumn是容量为1的花费

现在容量是u,那么还需要乘以u

所 以 花 费 是 s u m n [ x ] ∗ u + p a t h [ x + 1 ] ∗ b 所以花费是sumn[x]*u+path[x+1]*b sumn[x]u+path[x+1]b

再 除 以 v 就 好 啦 ! 因 为 1 到 n 只 需 要 1 的 流 量 而 不 是 v 的 流 量 再除以v就好啦!因为1到n只需要1的流量而不是v的流量 v!1n1v

#include 
using namespace std;
#define int long long
const int maxn=2e5+10;
const int inf=1e9;
int n,m,s,t;
struct edge{
	int to,nxt,flow,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow,int w){
	d[++cnt]=(edge){v,head[u],flow,w},head[u]=cnt;
	d[++cnt]=(edge){u,head[v],0,-w},head[v]=cnt;
}
int dis[maxn],vis[maxn],top,path[maxn],sumn[maxn],flow[maxn],pre[maxn];
bool spfa()
{
	for(int i=0;i<=t;i++)	dis[i]=inf,vis[i]=0;
	dis[s]=0; flow[s]=inf;
	queueq; q.push( s );
	while( !q.empty() )
	{
		int u=q.front(); q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v=d[i].to;
			if( d[i].flow&&dis[v]>dis[u]+d[i].w )
			{
				dis[v]=dis[u]+d[i].w;
				flow[v]=min( flow[u],d[i].flow );
				pre[v]=i;
				if( !vis[v] )	vis[v]=1,q.push( v );
			}
		}
	}
	return dis[t]!=inf;
}
void dinic()
{
	while( spfa() )
	{
		int x=t;
		path[++top]=dis[t];	//第top条增广路的花费,流量一定是1,所以没乘flow[t] 
		while( x!=s )
		{
			int i=pre[x];
			d[i].flow-=1;
			d[i^1].flow+=1;
			x=d[i^1].to;
		}
	}
}
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}
signed main()
{
	while( cin >> n >> m )
	{
		for(int i=1;i<=m;i++ )
		{
			int l,r,w;
			scanf("%lld%lld%lld",&l,&r,&w);
			add(l,r,1,w);
		}
		s=1,t=n;
		dinic();
		for(int i=1;i<=top;i++)	sumn[i]=sumn[i-1]+path[i];
		int q; cin >> q;
		while( q-- )
		{
			int u,v;
			scanf("%lld%lld",&u,&v);
			if( u==0 )
			{
				cout << "NaN\n";
				continue;
			}
			int x=v/u,y=v%u;
			if( x>top || ((x+1)>top&&y) )	cout << "NaN\n";
			else
			{
				int cost=sumn[x]*u+path[x+1]*y;
				int yin=gcd(cost,v);
				printf("%lld/%lld\n",cost/yin,v/yin);
			}
		}
		for(int i=1;i<=n;i++)	head[i]=0;
		cnt=1,top=0;
	}
}

你可能感兴趣的:(图论icpc多校真题)