「二进制分组」HDU 6166 Senior Pan
简述
原题面:hdu
\(T\) 组数据,每次给定一张 \(n\) 个点 \(m\) 条边的有向图,边有边权。
给定 \(k\) 个关键点,求 \(k\) 个关键点两两距离的最小值。
\(1\le T\le 5\),\(1\le k\le n\le 10^5\),\(1\le m\le 10^5\),\(1\le\) 边权 \(\le 10^5\)。
6S,128MB。
分析
二进制分组 + 多源最短路。
二进制分组多源最短路裸题。
枚举二进制位,根据 关键点的编号 这一位是否为 1 进行分组,将这一位为 1 的作为起点,跑多源最短路。统计到达这一位不为 1 的关键点的最短路长度。
由于所有关键点的编号都不同,能保证两个关键点在某一次分组中不在同一集合里,因此可以统计到所有点对的情况。
由于是有向图,注意正反各跑一遍。
使用堆优化 Dijkstra
实现,时间复杂度 \(O((n+m)\log (n+m)\log k)\)。
注意常数。
实现
//知识点:多源最短路
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <queue>
#define LL long long
#define pr std::pair
#define mp std::make_pair
const int kN = 1e5 + 10;
const int kM = 2e5 + 10;
const LL kInf = 1e12 + 2077;
//=============================================================
int n, m, k, pos[kN];
int e_num, head[kN], v[kM], w[kM], ne[kM];
LL ans, dis[kN];
bool vis[kN], start[kN];
//=============================================================
inline int read() {
int f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Chkmax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(LL &fir_, LL sec_) {
if (sec_ < fir_) fir_ = sec_;
}
void Init() {
ans = kInf;
e_num = 0;
memset(head, 0, sizeof (head));
}
void AddEdge(int u_, int v_, int w_) {
v[++ e_num] = v_;
w[e_num] = w_;
ne[e_num] = head[u_];
head[u_] = e_num;
}
void Dijkstra() {
std::priority_queue <pr <LL, int> > q;
memset(dis, 63, sizeof (dis));
memset(vis, 0, sizeof (vis));
for (int i = 1; i <= k; ++ i) {
if (! start[i]) continue ;
dis[pos[i]] = 0;
q.push(mp(0, pos[i]));
}
while (! q.empty()) {
int u_ = q.top().second;
q.pop();
if (vis[u_]) continue ;
vis[u_] = true;
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i], w_ = w[i];
if (1ll * dis[u_] + w_ < 1ll * dis[v_]) {
dis[v_] = 1ll * dis[u_] + 1ll * w_;
q.push(mp(-dis[v_], v_));
}
}
}
}
//=============================================================
int main() {
int T = read();
for (int nowT = 1; nowT <= T; ++ nowT) {
Init();
n = read(), m = read();
for (int i = 1; i <= m; ++ i) {
int u_ = read(), v_ = read(), w_ = read();
AddEdge(u_, v_, w_);
}
k = read();
for (int i = 1; i <= k; ++ i) pos[i] = read();
for (int bit = 0; (1 << bit) <= k; ++ bit) {
for (int j = 1; j <= k; ++ j) {
start[j] = (j & (1 << bit));
}
Dijkstra();
for (int j = 1; j <= k; ++ j) {
if (! start[j]) Chkmin(ans, dis[pos[j]]);
}
for (int j = 1; j <= k; ++ j) {
start[j] = !start[j];
}
Dijkstra();
for (int j = 1; j <= k; ++ j) {
if (! start[j]) Chkmin(ans, dis[pos[j]]);
}
}
printf("Case #%d: %lld\n", nowT, ans);
}
return 0;
}
作者@Luckyblock,转载请声明出处。