题意

给定一个有 \(N\) 个点 \(M\) 条边的无向图, 每条无向边 最多只能经过一次 .

对于边 \((u, v)\) , 从 \(u\) 到 \(v\) 的代价为 \(a\) , 从 \(v\) 到 \(u\) 的代价为 \(b\) , 其中 \(a\) 和 \(b\) 不一定相等.

求一个包含 \(1\) 号点的有向环, 使得环上代价之和最小.

\(N \le 3 \times 10^4 , M \le 10^5 , 1 \le a, b \le 10^4\) , 保证没有重边和自环 .

题解

考虑一条包含 \(1\) 的有向环, 一定是 \(1 \to x \to \cdots \to y \to 1\) 这样子. \((x \not = y)\)

那么我们可以考虑一个很显然的暴力:枚举 \(x, y\) 然后做最短路, 但是这样显然太慢了.

但是这里的最短路是可以 “并行” 地求的. 也就是说, 如果给定两个不相交的点集 \(\mathcal{A}, \mathcal{B}\) , 那么我们可以用一次最短路的时间求出所
有点对 \((x, y)\) 满足 \(x \in \mathcal{A}, y \in \mathcal{B}\) 的最短路的最小值.

具体地, 我们把 \(1\) 号点拆成两个点, 一个作为源点只连向 \(\mathcal{A}\) 中的点, 另一个作为汇点只被 \(\mathcal{B}\) 中的点连向.

然后这里需要一个二进制拆分的技巧: 在与 \(1\) 相邻的那些点中,每次考虑它们二进制下的第 \(k\) 位, 将这一位为 \(0\) 的放入 \(A\) , 为 \(1\) 的放入 \(\mathcal{B}\) , 那么只需 \(\log N\) 次, 我们便可以考虑到每一对.

以上全部摘自 __debug 的 PPT 。

这个最短路可以用 Spfa 求,但实测要比 Dijkstra 慢几倍。。为了求稳,还是用 Dijkstra 吧233

所以最后的复杂度就是 \(\mathcal O((N + M) \log^2 N)\)

总结

对于一类考虑点对贡献,并且很多对可以并行求,且重复计算没有影响的问题,能考虑二进制拆分技巧,对于每一位分别考虑。

将整体分成两组,最后计算贡献,能大幅度降低时间复杂度。

新套路 get

代码

特别好写233

#include <bits/stdc++.h>#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define fir first
#define sec second
#define mp make_pairusing namespace std;inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}inline int read() {int x = 0, fh = 1; char ch = getchar();for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);return x * fh;
}void File() {
#ifdef zjp_shadowfreopen ("2069.in", "r", stdin);freopen ("2069.out", "w", stdout);
#endif
}const int N = 5010, M = 10100 * 2, inf = 0x7f7f7f7f;int Head[N], Next[M], to[M], val[M], e = 0;inline void add_edge(int u, int v, int w) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = w; }priority_queue<pair<int, int> > P;
int dis[N], S, T; bitset<N> vis;int Dijkstra() {Set(dis, inf); dis[S] = 0; P.push(mp(0, S)); vis.reset();while (!P.empty()) {int u = P.top().sec; P.pop(); if (vis[u]) continue ; vis[u] = true;for (int i = Head[u]; i; i = Next[i]) {int v = to[i]; if (chkmin(dis[v], dis[u] + val[i])) P.push(mp(- dis[v], v));}}return dis[T];
}struct Edge { int u, v, a, b; } lt[M]; int n, m;
void Rebuild(int cur, int flag) {Set(Head, 0); e = 0; S = 1; T = n + 1;For (i, 1, m) {int u = lt[i].u, v = lt[i].v, a = lt[i].a, b = lt[i].b;if (u == 1) {if ((v & cur) ^ flag) add_edge(S, v, a); else add_edge(v, T, b);} else add_edge(u, v, a), add_edge(v, u, b);}
}int main () {File();n = read(); m = read();For (i, 1, m) {int u = read(), v = read(), a = read(), b = read();if (u > v) swap(u, v), swap(a, b);lt[i] = (Edge) {u, v, a, b};}int ans = inf;for (int bit = 1; bit <= n; bit <<= 1) {Rebuild(bit, 0), chkmin(ans, Dijkstra());Rebuild(bit, bit), chkmin(ans, Dijkstra());}printf ("%d\n", ans);return 0;
}

转载于:https://www.cnblogs.com/zjp-shadow/p/9561449.html

BZOJ 2069: [POI2004]ZAW(Dijkstra + 二进制拆分)相关推荐

  1. BZOJ 2069 POI2004 ZAW 堆优化Dijkstra

    题目大意:给定一张无向图,每条边从两个方向走各有一个权值,求从点1往出走至少一步之后回到点1且不经过一条边多次的最短路 显然我们需要从点1出发走到某个和点1相邻的点上,然后沿最短路走到另一个和点1相邻 ...

  2. BZOJ.2069.[POI2004]ZAW(最短路Dijkstra 按位划分)

    题目链接 \(Description\) 给定一张带权图(边是双向的,但不同方向长度不同).求从1出发,至少经过除1外的一个点,再回到1的最短路.点和边不能重复经过. \(n\leq5000,m\le ...

  3. 【刷题】BZOJ 2069 [POI2004]ZAW

    Description 在Byte山的山脚下有一个洞穴入口. 这个洞穴由复杂的洞室经过隧道连接构成. 洞穴的入口是一条笔直通向"前面洞口"的道路. 隧道互相都不交叉(他们只在洞室相 ...

  4. 2069: [POI2004]ZAW

    2069: [POI2004]ZAW 链接 题意: 给定一张带权图(边是双向的,但不同方向长度不同).求从1出发,至少经过除1外的一个点,再回到1的最短路.点和边不能重复经过. n≤5000,m≤10 ...

  5. bzoj 2096 [POI2004]ZAW——二进制枚举

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2069 可以把直接相连的点分成  从1点出的一部分  和  走向1点的一部分.多起点最短路就和 ...

  6. 牛客算法周周练2 B Music Problem(DP,抽屉原理,二进制拆分)

    链接:https://ac.nowcoder.com/acm/contest/5203/B 来源:牛客网 题目描述 Listening to the music is relax, but for o ...

  7. ACwing 5. 多重背包问题 II(二进制拆分+DP)

    文章目录 1. 题目 2. 解题 1. 题目 有 N 种物品和一个容量是 V 的背包. 第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi. 求解将哪些物品装入背包,可使物品体积总和不超过 ...

  8. 多重背包的二进制拆分法

    在多重背包的直接拆分法中,个数为$c[i]$的物体被拆成$c[i]$种不同的物体 这样就使得物体的种类增加了很多,使得算法效率很低. 上述方法把$c[i]$拆成$c[i]$个1,于是任意选择可以表示出 ...

  9. 多重背包问题和“二进制拆分”

    预告:我用两年写的新书<算法竞赛>,已于2022年2月交给清华大学出版社,预计于2022年7月出版.<算法竞赛>是一本"大全",内容覆盖"基础-中 ...

最新文章

  1. 天气预报中的风向到底有啥用?
  2. 文件、目录——Linux基本命令(5)
  3. 手写一个合格的前端脚手架
  4. 华为杯数学建模2020获奖名单_我校在2020年全国大学生数学建模竞赛中再获佳绩(内附获奖名单)...
  5. 组装生成HashMap结构类型
  6. idea 注册码 实测可用
  7. 现代汉语常用汉字3500表
  8. 新曼联:弗格森制造 第七章 欧战惊喜 之4 安心之选:埃尔文
  9. Adadelta理解
  10. 高性价比运维工具推荐
  11. 怎么复制黑苹果config配置_Catia中端电脑配置推荐(黑苹果)
  12. 如何完美实现微信自动发朋友圈自动添加好友等等
  13. 在linux运行php文件
  14. JS验证18位身份证号的正确性
  15. 计算机cpu的定义,CPU是什么?
  16. python画小树_如何用Python画一颗小树?
  17. Unreal Engine 4 —— Smear Frame效果的实现与分析
  18. java 根据经纬度获取区域面积
  19. 【机器学习】numpy实现NAG(Nesterov accelerated gradient)优化器
  20. 情境领导者-第六章、产生胜利者 故事

热门文章

  1. 基于DAMO-YOLO的RepGFPN多尺度特征融合的YOLOv5、YOLOv7、Faster RCNN、FCOS、CenterNet等目标检测器改进
  2. 北四村的蚁族程序员:面朝西二旗
  3. jdbc 批量执行sql
  4. 联想笔记本声音太小怎么办_电脑声音特别的小是怎么回事?我的笔记本
  5. 输入在第一行中给出一个正整数n(1)。第二行输入n个整数,用空格分开。在一行中输出最大值及最大值的最小下标,中间用一个空格分开。
  6. JVM-13. 垃圾回收器
  7. excel空白单元格自动下下填充上一个单元格的值
  8. 电脑重装系统后Word表格自动换行的方法
  9. li相关整理:如何改变li前面点的颜色和如何去掉li的点
  10. wp文件转shp_ArcGIS教程:MapGIS转换shp攻略