BZOJ 2069: [POI2004]ZAW(Dijkstra + 二进制拆分)
题意
给定一个有 \(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_pair
using 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_shadow
freopen ("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;
}
BZOJ 2069: [POI2004]ZAW(Dijkstra + 二进制拆分)的更多相关文章
- BZOJ 2069 POI2004 ZAW 堆优化Dijkstra
题目大意:给定一张无向图.每条边从两个方向走各有一个权值,求从点1往出走至少一步之后回到点1且不经过一条边多次的最短路 显然我们须要从点1出发走到某个和点1相邻的点上,然后沿最短路走到还有一个和点1相 ...
- BZOJ.2069.[POI2004]ZAW(最短路Dijkstra 按位划分)
题目链接 \(Description\) 给定一张带权图(边是双向的,但不同方向长度不同).求从1出发,至少经过除1外的一个点,再回到1的最短路.点和边不能重复经过. \(n\leq5000,m\le ...
- 【刷题】BZOJ 2069 [POI2004]ZAW
Description 在Byte山的山脚下有一个洞穴入口. 这个洞穴由复杂的洞室经过隧道连接构成. 洞穴的入口是一条笔直通向"前面洞口"的道路. 隧道互相都不交叉(他们只在洞室相 ...
- 2069: [POI2004]ZAW
2069: [POI2004]ZAW 链接 题意: 给定一张带权图(边是双向的,但不同方向长度不同).求从1出发,至少经过除1外的一个点,再回到1的最短路.点和边不能重复经过. n≤5000,m≤10 ...
- bzoj 2096 [POI2004]ZAW——二进制枚举
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2069 可以把直接相连的点分成 从1点出的一部分 和 走向1点的一部分.多起点最短路就和 ...
- BZOJ2069: [POI2004]ZAW
2069: [POI2004]ZAW Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 303 Solved: 138[Submit][Status][D ...
- 【最短路】【dijkstra】【二进制拆分】hdu6166 Senior Pan
题意:给你一张带权有向图,问你某个点集中,两两结点之间的最短路的最小值是多少. 其实就是dijkstra,只不过往堆里塞边的时候,要注意塞进去它是从集合中的哪个起始点过来的,然后在更新某个点的答案的时 ...
- hdu 2844 coins(多重背包 二进制拆分法)
Problem Description Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. On ...
- HDU 4135:Co-prime(容斥+二进制拆分)
Co-prime Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
随机推荐
- 书城项目第五阶段---book表的curd
JavaEE三层架构分析 MVC
- ios不触发事件也能播放音频
ios不触发事件也能播放音频. 首先界面初始化预加载一个没有声音的音频,代码如下: html: js: $(function(){ $("#start_audio")[0].pla ...
- css中如何做到容器按比例缩放
需求: 一般在响应式中,我们会要求视频的宽高比为16:9或4:3,这么一来就比较头大了.当用户改变浏览器宽度的时候(改变高度不考虑),视频的宽度变了,那么高度也得根据我们要求的16:9或4:3改变. ...
- class用法
自 PHP 5.5 起,关键词 class 也可用于类名的解析.使用 ClassName::class 你可以获取一个字符串,包含了类 ClassName 的完全限定名称.这对使用了 命名空间 的类尤 ...
- 使用PSR-4配合composer autoload 自动加载文件夹
require 文件很麻烦,使用PSR-4搭配composer一次加载,终生受用. 感觉类似java中的import了,自己先记录一下最近理解的. 用composer管理自己的包吧 安装compose ...
- Spark源码编译,官网学习
这里以spark-1.6.0版本为例 官网网址 http://spark.apache.org/docs/1.6.0/building-spark.html#building-with-build ...
- C# Note17: 使用Ionic.Zip.dll实现解压缩文件
首先下载ionic.Zip.dll,然后在项目中添加该引用,之后就可以在cs中使用了: using Ionic.Zip; #region Ionic.Zip压缩文件 private readonly ...
- If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
学习Spring Boot 过程中遇到了下列这个问题 Description: Failed to configure a DataSource: 'url' attribute is not spe ...
- 关于对ProgressBar定义模板的一些总结
在之前的博客中曾经写到了一篇关于如何定义圆形进度条的文章,今天就ProgressBar再来进行一些总结,首先来介绍一下ProgressBar的结构,ProgressBar控件的模板有两个部分,我们在定 ...
- linux中一些特殊的中文文件不能删除问题
例: [root@iZ2zecl4i8oy1rvs00dqzeZ tmp]# ,),(,,' [root@iZ2zecl4i8oy1rvs00dqzeZ tmp]# echo "rm -rf ...