「二进制分组」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;
}
posted @ 2020-11-13 17:12  Luckyblock  阅读(86)  评论(0编辑  收藏  举报