题面

给定一张分层有向图,有 \(n\) 层,每层有 \(m\) 个点。只有从第 \(i\) 层的点连向第 \(i + 1\) 层的点的连边。

记 \(A(i,j)\) 表示从第 \(i\) 层的某些点出发到第 \(j\) 层的某些点的点不相交路径集合的最大大小。求

\[\sum_{i=1}^n\sum_{j=i+1}^n A(i,j)
\]

其中,\(n\le 4\times10^4,m\le10\)。


解析

注意到 \(m\) 很小,于是首先有一个暴力状压的思路。\(f(i,S)\) 表示从第 \(i\) 层的 \(S\) 点集出发,要求这 \(|S|\) 条路径都能到达第 \(j\) 层,\(j\) 最大是多少。

可以 DFS 出所有第 \(i\) 层和第 \(i + 1\) 层之间的转移,总复杂度 \(\mathcal{O}(2^{2m}n)\)。统计答案则可以记 \(g(i,k)\):

\[g(i,k)=\max_{|S|=k}f(i,S)
\]

则 \(A(i, j) = k\)(\(g(i, k) \le j \le g(i, k + 1)\)),可以快速统计答案。

考虑这样 DP 的本质是什么。假如把原问题建网络流,只需要把每个点拆点限制流量,就相当于求两层之间的最大流。

而状压 DP 反映的是这样一个结论:从第 \(i\) 层出发,可以在任意位置结束,尽可能流到较大的层;在这样的前提下流最大流,则 \(A(i,j)\) 为流经第 \(j\) 层的流量。

相当于一个费用流,两层之间费用为 \(1\)。(实际上也没有这样去实现代码,只是方便理解)

考虑到这个流网络非常特殊,可以模拟流的过程。

  • 由于可以在任何位置结束,所以一定是满流的,只需要每次找到残留网络的最长路——即能够到达的最深的层
  • 可以直接 BFS,由于费用只和点的层数有关,所以一个点不需要多次更新,过程中维护点的访问标记。
  • 记录增广路的前驱,在找到层数最大的增广路后逆序还原增广路,更新残留网络。
  • 由于流网络分层,不可能从较大的层流向较小的层,于是将源点从第 \(i\) 层换到第 \(i + 1\) 层时,可以直接继承残留网络,删去 \(i\) 和 \(i + 1\) 层之间的连边。

最后就是时间复杂度的问题了。注意到假如在一次增广时,找到的增广路层数为 \(p\),则最多访问 \(pk\) 个点,每个点转移复杂度 \(\mathcal{O}(k)\),则该次增广复杂度为 \(\mathcal{O}(pk^2)\)。

该次增广后,流网络的总流量增加 \(\mathcal{O}(p)\)(手动模拟一下,可以发现即使发生退流,由于我们取层数最大的增广路,退掉的流也会补回来,该次增广仍会使流网络总流量增加 \(p\)),而流网络总流量 \(\mathcal{O(nk)}\),所以 \(\mathcal{O}(\sum pk^2)=\mathcal{nk^3}\)。

模拟细节见代码。


源代码

/* Lucky_Glass */
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> typedef long long llong;
typedef std::pair<int, int> pii; int rin(int &r) {
int c = getchar(); r = 0;
while (c < '0' || '9' < c) c = getchar();
while ('0' <= c && c <= '9') r = r * 10 + (c ^ '0'), c = getchar();
return r;
} const int N = 4e4 + 10, K = 10; int n, m, nw, tot_flw;
int lnk[N << 1][K], lnk_i[N << 1][K], vis[N << 1];
int elg2[(1 << K) + 10], flw_cnt[N];
pii las_pnt[N << 1][K]; void extend(const int &sx, const int &sy) {
int ed_x = sx, ed_y = sy; int clear_vis_pos = sx;
vis[sx] = 1 << sy;
/* 此次增广经过的第 i 层点(防止环流?) */ std::queue<pii> que;
que.emplace(sx, sy);
while (!que.empty()) {
int ux = que.front().first, uy = que.front().second;
que.pop();
if ((ux & 1) && (ux >> 1) > (ed_x >> 1))
ed_x = ux, ed_y = uy;
/* 向下一层 */
if (ux < nw) {
if (clear_vis_pos == ux) vis[++clear_vis_pos] = 0;
int rem = lnk[ux][uy] ^ (lnk[ux][uy] & vis[ux + 1]);
while (rem) {
int vy = elg2[rem & -rem];
vis[ux + 1] |= 1 << vy;
/* las_pnt 记录转移点,便于还原增广路 */
las_pnt[ux + 1][vy] = std::make_pair(ux, uy);
que.emplace(ux + 1, vy);
rem ^= rem & -rem;
}
}
/* 经过逆向边返回上一层,lnk_i 存储反向边 */
if (ux > sx) {
int rem = lnk_i[ux][uy] ^ (lnk_i[ux][uy] & vis[ux - 1]);
while (rem) {
int vy = elg2[rem & -rem];
vis[ux - 1] |= 1 << vy;
las_pnt[ux - 1][vy] = std::make_pair(ux, uy);
que.emplace(ux - 1, vy);
rem ^= rem & -rem;
}
}
}
/* 更新每一层的流量,更新答案 */
for (int i = sx >> 1, lmt_i = ed_x >> 1; i <= lmt_i; ++i)
++flw_cnt[i], ++tot_flw;
/* 还原增广路,更新残留网络 */
while (ed_x != sx || ed_y != sy) {
// printf("(%d, %d) <- ", ed_x, ed_y);
int lasx = las_pnt[ed_x][ed_y].first,
lasy = las_pnt[ed_x][ed_y].second;
if (ed_x == lasx + 1) {
lnk[lasx][lasy] ^= 1 << ed_y;
lnk_i[ed_x][ed_y] ^= 1 << lasy;
} else {
lnk[ed_x][ed_y] ^= 1 << lasy;
lnk_i[lasx][lasy] ^= 1 << ed_y;
}
ed_x = lasx, ed_y = lasy;
}
// printf("(%d, %d)\n", sx, sy);
}
void init() {
for (int i = 0; i <= K; ++i)
elg2[1 << i] = i;
}
int main() {
freopen("flow.in", "r", stdin);
freopen("flow.out", "w", stdout);
// freopen(".\\input\\input.in", "r", stdin);
init();
rin(n), rin(m);
nw = (n << 1) - 1;
for (int i = 1; i < nw; ++i)
if (i & 1) {
static char inp[K << 1];
for (int j = 0; j < m; ++j) {
scanf("%s", inp);
for (int k = 0; k < m; ++k)
lnk[i][j] |= (inp[k] ^ '0') << k;
}
} else {
for (int j = 0; j < m; ++j)
lnk[i][j] = 1 << j;
} llong ans = 0;
for (int i = 1; i < n; ++i) {
for (int j = 0; j < m; ++j)
extend(std::max(1, (i - 1) << 1), j);
/*
* 注意一定是从 (i - 1) << 1 开始
* 通过 ((i - 1) << 1) -> (((i - 1) << 1) - 1) 是否有边
* 限制每个点只能用一次
* 因为第一层不存在访问多次,可以直接从 1 开始跑
*/
ans += (tot_flw -= flw_cnt[i - 1]);
}
printf("%lld\n", ans);
return 0;
}

THE END

Thanks for reading!

扬起远航的帆
谁在轻声呼唤
萦绕在耳畔
到达心的彼岸
一切宛如梦幻
又归于平淡

——《巫山云》By Snapmod / 诗岸

> Link 巫山云 - 网易云

「SOL」网络流flow (模拟赛)的更多相关文章

  1. 「NOIP2017」「LuoguP3952」 时间复杂度(模拟,栈

    题目描述 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序 ...

  2. 「CF85E」 Guard Towers

    「CF85E」 Guard Towers 模拟赛考了这题的加强版 然后我因为初值问题直接炸飞 题目大意: 给你二维平面上的 \(n\) 个整点,你需要将它们平均分成两组,使得每组内任意两点间的曼哈顿距 ...

  3. 「CSP-S模拟赛」2019第四场

    「CSP-S模拟赛」2019第四场 T1 「JOI 2014 Final」JOI 徽章 题目 考场思考(正解) T2 「JOI 2015 Final」分蛋糕 2 题目 考场思考(正解) T3 「CQO ...

  4. 「NOWCODER」CSP-S模拟赛第3场

    「NOWCODER」CSP模拟赛第3场 T1 货物收集 题目 考场思路即正解 T2 货物分组 题目 考场思路 题解 60pts 算法:一维 DP 100pts 算法:一维 DP ?线段树 + 单调栈 ...

  5. #10471. 「2020-10-02 提高模拟赛」灌溉 (water)

    题面:#10471. 「2020-10-02 提高模拟赛」灌溉 (water) 假设只有一组询问,我们可以用二分求解:二分最大距离是多少,然后找到深度最大的结点,并且把它的\(k\)倍祖先的一整子树删 ...

  6. #10470. 「2020-10-02 提高模拟赛」流水线 (line)

    题面:#10470. 「2020-10-02 提高模拟赛」流水线 (line) 题目中的那么多区间的条件让人感觉极其难以维护,而且贪心的做法感觉大多都能 hack 掉,因此考虑寻找一些性质,然后再设计 ...

  7. 「NOIP模拟赛」数位和乘积(dp,高精)

    统计方案数,要么组合数,要么递推(dp)了. 这是有模拟赛历史以来爆炸最狠的一次 T1写了正解,也想到开long long,但是开错了地方然后数组开大了结果100->0 T3看错题本来简单模拟又 ...

  8. 「Vijos 1284」「OIBH杯NOIP2006第二次模拟赛」佳佳的魔法阵

    佳佳的魔法阵 背景 也许是为了捕捉猎物(捕捉MM?),也许是因为其它原因,总之,佳佳准备设计一个魔法阵.而设计魔法阵涉及到的最关键问题,似乎就是那些带有魔力的宝石的摆放-- 描述 魔法阵是一个\(n ...

  9. 「CSP-S模拟赛」2019第三场

    目录 T1 「POI2007」山峰和山谷 Ridges and Valleys 题目 考场思路(几近正解) 正解 T2 「JOI 2013 Final」 现代豪宅 题目 考场思路(正解) T3 「SC ...

  10. 「CSP-S模拟赛」2019第一场

    目录 T1 小奇取石子 题目 考场思路 正解 T2 「CCO 2017」专业网络 题目 考场思路 题解 T3 「ZJOI2017」线段树 题目 考场思路 正解 这场考试感觉很奇怪. \(T1.T2\) ...

随机推荐

  1. Office 2016 专业版打开Excel空白解决方案

    一.打开注册表 1.Win+R 输入 regedit 回车 2.找到路径 HKEY_CLASSES_ROOT\Excel.Sheet.12\shell\Open\command 更改默认值为 &quo ...

  2. Javaweb-1note C/S B/S HTML CSS javaScript一点点语法

    ------------恢复内容开始------------ Java web概念: *javaweb:使用java语言开发基于互联网的项目 *软件架构: 1.c/s:Clienr/Server 客户 ...

  3. 举例说明postman接口测试

    接口测试的本质就是接口的数据和数据库里的数据作对比 接口测试,可以理解为测的是后端的程序,而系统测试的时候,测试的是前端的程序,前端只有在满足条件的时候才会调到接口,所以接口测试可以测得更全面更准确 ...

  4. ClickHouse exception, code: 62, host: hadoop102, port: 8123; Code: 62, e.displayText() = DB::Exception: Syntax error: failed at position 183 (end of query):

    报错 ClickHouse exception, code: 62, host: hadoop102, port: 8123; Code: 62, e.displayText() = DB::Exce ...

  5. JZOJ 4496. 【GDSOI 2016】第一题 互补约数

    \(\text{Problem}\) 求 \[\sum_{i=1}^n \sum_{d|n} \gcd(d, \frac{i}{d}) \] 有 \(n \le 10^{11}\) \(\text{A ...

  6. JZOJ 4366. 【GDKOI2016】项链

    \(\text{Problem}\) 给出一个项链,删去连续的一部分,使剩下的对称,且长度最长 \(\text{Analysis}\) 可以发现,剩下的合法项链一定是由两个回文串接起来(由对称性质可知 ...

  7. 【CQOI2011】动态逆序对

    分析 近乎裸的 \(cdq\) 分治数点问题 我们考虑一个数被删去,它对删后区间逆序对个数的影响就是减去现存序列中前面比它大的个数再减去现存序列中后面比它小的个数 那么我们考虑如何处理时间限制 既然是 ...

  8. 使用 Three.js 的 3D 制作动画场景

    推荐:将 NSDT场景编辑器 加入你的3D开发工具链. 由于 GSL 语法的复杂性,对于许多开发人员来说 WebGL 是一个未知的领域.但是有了 Three.js,在浏览器中 3D 的实现变得简单.下 ...

  9. Revit如何给模型绑定动画的教程

    推荐:将 NSDT场景编辑器 加入你的3D开发工具链. Revit模型完成后,为了展示成果,有时需要做动画,本文章将教大家如何在3dsmax中给塔吊族模型绑定旋转动画,并导入到Lumion当中使用. ...

  10. Kali配置gmssl密码算法库

    Kali配置gmssl密码算法库 一.密码算法库的下载 https://github.com/guanzhi/GmSSL/releases 二.安装配置 1 解压 把刚刚下载的GmSSL 3.0.0. ...