Minimum-cost Flow

题目:给n个点,m条边。接下来m行包含(a,b,c),即a,b之间有单位流量代价为c的边。接下来有q个问题,每个问题给定(x,y),即假设每条边的容量为x/y时,从点1到点n流1单位的流量,最少的花费是多少,如果无法从点1到点n流1单位的流量则输出“NaN”。

思路:首先我们需要想到一个结论,每条边最多只能使用一次,这个自己比划一下就可以,其中包含贪心的想法。得到这个结论后,我们发现整张图就变成了流量为1的网络流。然后,根据(x,y)说明我们至少需要 y / x + (y % x != 0)条从1到n的最少花费路径,这个我们可以通过最小费用流预处理得到所有能够得到的最小花费路径,并记录每条路径的最小花费。这样,对于每个询问(x,y),我们只需要取前y / x小的边,如果y % x != 0,则从后面的边补充一些即可。

补充:为什么我们可以直接这样直接取路径,看上图,我们得到的最小花费应该是两个:3,21。这是懂得网络流算法都能看出的,如果我们有组询问(2,3),我们可以得到答案应该是ans = 3 * 2/3 + 21 * 1/3 = 9。但这两条路分别是1->2->3->4和1->3->2->4得到的,会感觉有点奇怪“这样就是答案”。其实我们需要回到网络流的反向边在算法中的用途"反流"和正向边可以把"反流"失去的流量再"补流",下面附一个来体现ans = 9是怎么得到的,然后思考。

 #include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring> using namespace std; #define ll long long
#define pb push_back
#define fi first
#define se second const int N = ;
const int M = ;
const int INF = 1e9 + 7e8;
struct edge
{
int to, nxt, cap, flow, w;
}e[M << ];
int head[N], d[N], vis[N], pre[N];
queue<int > que;
int a[N], b[N];
int n, m, tot, s, t, cnt; inline void add(int u, int v, int w)
{
e[tot].to = v; e[tot].w = w; e[tot].cap = ;
e[tot].flow = ; e[tot].nxt = head[u]; head[u] = tot++;
e[tot].to = u; e[tot].w = -w; e[tot].cap = ;
e[tot].flow = ; e[tot].nxt = head[v]; head[v] = tot++;
} bool spfa()
{
for(int i = ; i <= n; ++i) d[i] = INF, vis[i] = false, pre[i] = -;
while(!que.empty()) que.pop();
d[s] = ; vis[s] = true; pre[s] = -;
que.push(s);
//printf("s = %d t = %d\n", s, t);
while(!que.empty()){
int now = que.front();
que.pop();
vis[now] = false; for(int o = head[now]; ~o; o = e[o].nxt){
if(e[o].cap - e[o].flow && d[e[o].to] > d[now] + e[o].w){
d[e[o].to] = d[now] + e[o].w;
pre[e[o].to] = o;
if(!vis[e[o].to]){
vis[e[o].to] = true;
que.push(e[o].to);
}
}
}
}
if(pre[t] == -) return false;
else return true;
} void mcmf()
{
while(spfa()){
int _min = INF;
for(int o = pre[t]; ~o; o = pre[e[o ^ ].to]){
_min = min(_min, e[o].cap - e[o].flow);
}
for(int o = pre[t]; ~o; o = pre[e[o ^ ].to]){
e[o].flow += _min;
e[o ^ ].flow -= _min;
}
//cout << _min << endl;
//cout << "dis = " << d[t] << endl;
a[++cnt] = d[t];
//cout << "d = " << d[t] << endl;
}
for(int i = ; i <= cnt; ++i) a[i] += a[i - ];
} ll GCD(ll a, ll b)
{
return b == ? a : GCD(b, a % b);
} void solve()
{
while(~scanf("%d%d", &n, &m)){
cnt = ;
for(int i = ; i <= n; ++i) head[i] = -; tot = ;
int x, y, w;
for(int i = ; i <= m; ++i){
scanf("%d%d%d", &x, &y, &w);
add(x, y, w);
}
s = ; t = n;
mcmf(); int q;
scanf("%d", &q);
while(q--){
int x, y;
scanf("%d%d", &x, &y);
if(x == ){
puts("NaN");
continue;
}
int gcd = GCD(x, y);
y /= gcd; x /= gcd; int need = y / x + (y % x != );
if(need > cnt){
puts("NaN");
continue;
} need = y / x;
ll up = (ll)a[need] * x;
ll down = y;
int remains = y % x;
up += (ll)(a[need + ] - a[need]) * remains;
gcd = GCD(up, down);
up /= gcd; down /= gcd;
printf("%lld/%lld\n", up, down);
}
}
} int main(){ solve(); return ;
}

2020牛客暑期多校训练营(第一场)H Minimum-cost Flow的更多相关文章

  1. 2020牛客暑期多校训练营 第二场 K Keyboard Free 积分 期望 数学

    LINK:Keyboard Free 我要是会正经的做法 就有鬼了. 我的数学水平没那么高. 三个同心圆 三个动点 求围成三角形面积的期望. 不会告辞. 其实可以\(n^2\)枚举角度然后算出面积 近 ...

  2. 2020牛客暑期多校训练营 第二场 J Just Shuffle 置换 群论

    LINK:Just Shuffle 比较怂群论 因为没怎么学过 置换也是刚理解. 这道题是 已知一个置换\(A\)求一个置换P 两个置换的关键为\(P^k=A\) 且k是一个大质数. 做法是李指导教我 ...

  3. 2020牛客暑期多校训练营 第二场 I Interval 最大流 最小割 平面图对偶图转最短路

    LINK:Interval 赛时连题目都没看. 观察n的范围不大不小 而且建图明显 考虑跑最大流最小割. 图有点稠密dinic不太行. 一个常见的trick就是对偶图转最短路. 建图有点复杂 不过建完 ...

  4. 2020牛客暑期多校训练营 第二场 C Cover the Tree 构造 贪心

    LINK:Cover the Tree 最受挫的是这道题,以为很简单 当时什么都想不清楚. 先胡了一个树的直径乱搞的贪心 一直过不去.后来意识到这类似于最经典长链剖分优化贪心的做法 然后那个是求最大值 ...

  5. 2020牛客暑期多校训练营 第二场 B Boundary 计算几何 圆 已知三点求圆心

    LINK:Boundary 计算几何确实是弱项 因为好多东西都不太会求 没有到很精通的地步. 做法很多,先说官方题解 其实就是枚举一个点 P 然后可以发现 再枚举一个点 然后再判断有多少个点在圆上显然 ...

  6. 2020牛客暑期多校训练营 第二场 A All with Pairs 字符串hash KMP

    LINK:All with Pairs 那天下午打这个东西的时候状态极差 推这个东西都推了1个多小时 (比赛是中午考试的我很困 没睡觉直接开肝果然不爽 一开始看错匹配的位置了 以为是\(1-l\)和\ ...

  7. 2020牛客暑假多校训练营 第二场 H Happy Triangle set 线段树 分类讨论

    LINK:Happy Triangle 这道题很容易. 容易想到 a+b<x a<x<b x<a<b 其中等于的情况在第一个和第三个之中判一下即可. 前面两个容易想到se ...

  8. 2020牛客暑期多校训练营(第一场)Easy Integration

    传送门:J. Easy Integration 题意:给你n,求这个积分,最后的结果分子是记为p,分母记为q. 求(p*q-1)mod 998244353. 题解:比赛完看到巨巨说这是贝塔函数,我一搜 ...

  9. 2020牛客暑期多校训练营(第四场)BCFH

    BCFH B. Basic God Problem 题意 给出c和n,求fc(n). 题解 递归到最后 fc 函数肯定等于1,那么就变成了求c被乘了几次,只要找到 x 最多能被分解成多少个数相乘就好了 ...

随机推荐

  1. 小师妹学JVM之:GC的垃圾回收算法

    目录 简介 对象的生命周期 垃圾回收算法 Mark and sweep Concurrent mark sweep (CMS) Serial garbage collection Parallel g ...

  2. 034.Kubernetes集群安全-Secret

    一 secret概述 1.1 secret作用 Secret对象,主要作用是保管私密数据,比如密码.OAuth Tokens.SSH Keys等信息.将这些私密信息放在Secret对象中比直接放在Po ...

  3. C# 实现定时/循环任务

    用C#实现定时/循环任务,需要使用到Quartz,在项目的NuGet包管理中可以找到并添加.一般还会伴随安装一个Log4Net,主要用它来记录在任务执行过程中遇到的问题.这边主要是讲实现定时/循环任务 ...

  4. [cpp]C++中的析构函数

    C++中的析构函数 简介 析构函数(Destructors),是对象的成员函数,没有返回值也没有参数,且一个类只有一个析构函数,当对象被销毁的时候调用,被销毁通常有这么几个情况. 函数执行结束 程序执 ...

  5. Spring Cloud面试题万字解析(2020面试必备)

    1.什么是 Spring Cloud? Spring cloud 流应用程序启动器是 于 Spring Boot 的 Spring 集成应用程序,提供与外部系统的集成.Spring cloud Tas ...

  6. trollcave解题

    这是第一次完整地进行模拟渗透,前前后后一共花了一天时间,花了点时间写了个writeup. 博主是个菜鸡,如果有大神看到,请轻喷...... writeup下载:https://hrbeueducn-m ...

  7. Halcon斑点分析BlobAnalysis解析

    斑点分析的算法非常简单:在图像中,相关对象的像素(也称为前景)通过其灰度值来识别.例如,图中示例显示了液体中的组织颗粒.这些粒子是明亮的,液体(背景)是暗的.通过选择明亮的像素(阈值),可以很容易检测 ...

  8. Python 简明教程 --- 7,Python 字符串

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 过早的优化代码是罪恶之源. -- Donald Knuth 目录 无论哪种编程语言,字符串处理都是最 ...

  9. linux主机连接sftp报错received unexpected end-of-file from SFTP server

    SFTP 连接主机失败,提示信息如下: 登陆目标主机,编辑查看 /etc/ssh/sshd_config 文件,找到 Subsystem 关键字 替换为 Subsystem sftp internal ...

  10. Oracle安装完成后修改服务器机器名,Oracle部分服务无法启动

    Oracle安装完成后修改服务器机器名,Windows server 2012 R2系统提示Oracle 11g下面3个服务无法启动: OracleDBConsoleorcl OracleOraDb1 ...