首页 > 其他分享 >LOJ #2876. 「JOISC 2014 Day2」水壶 题解

LOJ #2876. 「JOISC 2014 Day2」水壶 题解

时间:2024-02-14 16:22:41浏览次数:30  
标签:std 2876 tx fa LOJ 题解 ty kMaxP int

Description

JOI 君所居住的 IOI 市以一年四季都十分炎热著称。

IOI 市被分成 \(H\) 行,每行包含 \(W\) 块区域。每个区域都是建筑物、原野、墙壁之一。
IOI 市有 \(P\) 个区域是建筑物,坐标分别为 \((A_1, B_1),\) \((A_2, B_2),\) \(\ldots,\) \((A_P, B_P)\)。
JOI 君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外。

JOI 君因为各种各样的事情,必须在各个建筑物之间往返。虽然建筑物中的冷气设备非常好,但原野上太阳非常毒辣,因此在原野上每走过一个区域都需要 1 升水。此外,原野上没有诸如自动售货机、饮水处之类的东西,因此 IOI 市的市民一般都携带水壶出行。大小为 \(x\) 的水壶最多可以装 \(x\) 升水,建筑物里有自来水可以将水壶装满。
由于携带大水壶是一件很困难的事情,因此 JOI 君决定携带尽量小的水壶移动。因此,为了随时能在建筑物之间移动,请你帮他写一个程序来计算最少需要多大的水壶。

现在给出 IOI 市的地图和 \(Q\) 个询问,第 \(i\) 个询问包含两个整数 \(S_i,\) \(T_i\),对于每个询问,请输出:要从建筑物 \(S_i\) 移动到 \(T_i\),至少需要多大的水壶?

数据范围:\(H,W\leq2000\),\(P,Q\leq2\times10^5\),\(1\leq S_i,T_i\leq P\)。

Solution

首先对于任意两个建筑物连一条边权为他们的距离的边,那么对于每个询问,显然就是求最小瓶颈生成树两点间的最大权值。

考虑如何建出这个最小生成树。

可以先 bfs 求出距离每个原野最近的建筑物,那么对于每个建筑物,他管辖的原野一定是个连通块,考虑对于连通块有相邻边的两个建筑物连边,然后跑 kruskal 最小生成树。

感性理解一下,这样做显然能够连出一个生成树,并且对于每个点都保存了离他最近的几条边,所以这样做肯定能求出最小生成树。

时间复杂度:\(O\left(HW+(P+Q)\log P\right)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxH = 2e3 + 5, kMaxP = 2e5 + 5, kMaxE = 4e6 + 5;
const int kDx[] = {0, 0, 1, -1}, kDy[] = {1, -1, 0, 0};

int h, w, p, q;
int a[kMaxP], b[kMaxP], fa[kMaxP][19], faw[kMaxP][19], dep[kMaxP], lg[kMaxP];
std::vector<std::pair<int, int>> G[kMaxP];
std::vector<std::tuple<int, int, int>> ed;
std::string s[kMaxH];

struct DSU {
  std::vector<int> fa;

  void init(int _n) { fa.resize(_n + 1), std::iota(fa.begin(), fa.end(), 0); }
  
  DSU() {}
  DSU(int _n) { init(_n); }

  int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
  }

  void unionn(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx != fy) fa[fx] = fy;
  }
} dsu;

void bfs() {
  static int dis[kMaxH][kMaxH], idx[kMaxH][kMaxH];
  for (int i = 1; i <= h; ++i) {
    for (int j = 1; j <= w; ++j) {
      dis[i][j] = idx[i][j] = -1;
    }
  }
  std::queue<std::tuple<int, int, int>> q;
  for (int i = 1; i <= p; ++i) {
    dis[a[i]][b[i]] = 0, idx[a[i]][b[i]] = i;
    q.emplace(a[i], b[i], i);
  }
  for (; !q.empty();) {
    auto [x, y, id] = q.front();
    q.pop();
    for (int k = 0; k < 4; ++k) {
      int tx = x + kDx[k], ty = y + kDy[k];
      if (tx < 1 || tx > h || ty < 1 || ty > w || s[tx][ty] == '#') continue;
      if (!~idx[tx][ty]) {
        dis[tx][ty] = dis[x][y] + 1, idx[tx][ty] = id;
        q.emplace(tx, ty, id);
      } else if (idx[tx][ty] != id) {
        ed.emplace_back(dis[x][y] + dis[tx][ty], id, idx[tx][ty]);
      }
    }
  }
}

void kruskal() {
  dsu.init(p);
  std::sort(ed.begin(), ed.end());
  for (auto [w, u, v] : ed) {
    if (dsu.find(u) != dsu.find(v)) {
      dsu.unionn(u, v), G[u].emplace_back(v, w), G[v].emplace_back(u, w);
    }
  }
}

void dfs(int u, int fa) {
  ::fa[u][0] = fa, dep[u] = dep[fa] + 1;
  for (int i = 1; i <= lg[p]; ++i) {
    ::fa[u][i] = ::fa[::fa[u][i - 1]][i - 1];
    faw[u][i] = std::max(faw[u][i - 1], faw[::fa[u][i - 1]][i - 1]);
  }
  for (auto [v, w] : G[u]) {
    if (v == fa) continue;
    faw[v][0] = w;
    dfs(v, u);
  }
}

void lca_prework() {
  lg[0] = -1;
  for (int i = 1; i <= p; ++i)
    lg[i] = lg[i >> 1] + 1;
  for (int i = 1; i <= p; ++i) {
    if (!dep[i]) {
      dfs(i, 0);
    }
  }
}

int getans(int x, int y, int id = 0) {
  if (dep[x] < dep[y]) std::swap(x, y);
  int ret = 0;
  for (int i = lg[p]; ~i; --i)
    if (dep[fa[x][i]] >= dep[y])
      ret = std::max(ret, faw[x][i]), x = fa[x][i];
  if (x == y) return ret;
  for (int i = lg[p]; ~i; --i) {
    if (fa[x][i] != fa[y][i]) {
      ret = std::max({ret, faw[x][i], faw[y][i]});
      x = fa[x][i], y = fa[y][i];
    }
  }
  return std::max({ret, faw[x][0], faw[y][0]});
}

void dickdreamer() {
  std::cin >> h >> w >> p >> q;
  for (int i = 1; i <= h; ++i) {
    std::cin >> s[i];
    s[i] = " " + s[i];
  }
  for (int i = 1; i <= p; ++i)
    std::cin >> a[i] >> b[i];
  bfs(), kruskal(), lca_prework();
  for (int i = 1; i <= q; ++i) {
    int s, t;
    std::cin >> s >> t;
    std::cout << (dsu.find(s) == dsu.find(t) ? getans(s, t, i) : -1) << '\n';
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:std,2876,tx,fa,LOJ,题解,ty,kMaxP,int
From: https://www.cnblogs.com/Scarab/p/18015264

相关文章

  • CF1931F Chat Screenshots 另一种题解
    题目链接:CF或者洛谷本题拓扑排序不再赘述,来说说字符串哈希怎么做这题。本篇以另一种角度剖析题目背景,并不追求最优,例如有些地方其实可以暴力判断,主要以构造的角度阐述,顺便感谢灵茶山的朋友的讨论。结论三个串及其以上必定能构造出最初的那个串。下面进行证明:首先一个串,显......
  • P8683题解
    首先,看题目描述,给定N个加号,与M个减号,要你求后缀表达式最大值,实际上就是求这些符号和数字排列起来的最大值。这题很容易让人想到贪心。但是呢,又怎么个贪心法呢?很容易看出来,我们可以先用sort进行这么一个排序,之后,我们对于前N大的数加起来,对于剩下的数就减去,于是代码大体就......
  • 麦肯锡问题解决流程-为希望提升水平的产品经理量身定制
    您是否想知道世界上最成功的产品经理如何始终如一地提供不仅满足而且超出预期的解决方案?秘密可能就在于世界上最负盛名的咨询公司之一麦肯锡公司所磨练的方法论。本文深入探讨了麦肯锡的问题解决流程,该流程专为希望提升水平的产品经理量身定制。01.麦肯锡方法:产品管理风......
  • SP34020 ADAPET - Ada and Pet 题解
    题目传送门前置知识前缀函数与KMP算法解法经检验样例,我们发现\(|S|k\)并不是最优答案。考虑利用luoguP4391[BOI2009]RadioTransmission无线传输结论的逆命题,首先必须要有一个完整的\(S\),然后将\(|S|-next_{S}\)复制\(k-1\)次即可。故\(|S|+(|S|-next_{|S|}......
  • P5350 序列 题解
    题目链接:P5350序列比较不难的可持久化文艺平衡树?有道弱化版:数组操作不过弱化版没有随机数据,很显然ODT会直接被卡,这题数据随机倒是能用ODT做一下,而且码量也比较小,可以自己写写,或者参照我给的弱化版我给了这题部分操作的ODT写法。我们还是来讲最实用的可持久化文艺平衡树......
  • UVA12467 Secret Word 题解
    题目传送门前置知识前缀函数与KMP算法解法考虑将\(S\)翻转后得到\(S'\),然后就转化为求\(S'\)的一个最长子串使得其是\(S\)的前缀。使用KMP求解即可。代码#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#defineullunsignedlonglong#d......
  • WGOI R1 真夏飞焰 题解.18014664
    【题目大意】给定序列\(a\),我们定义序列\(a,b\)是「\(k\)相似」的,当且仅当对于\(a\)中每一个四元组\((l_1,r_1,l_2,r_2)\),若满足\(r_1-l_1+1=r_2-l_2+1\lek,l_2=r_1+1,a_{l_1\ldotsr_1}=a_{l_2\ldotsr_2}\),则有\(b_{l_1\ldotsr_1}=b_{l_2\ldot......
  • CF609D题解
    Gadgetsfordollarsandpounds题目传送门题解给一个单\(\log\)题解。“求最早完成买\(k\)样东西的天数。”——很明显的二分答案。在检验函数中,我们应当把前\(k\)小的物品费用之和与总金额作比较,其它题解大多使用直接排序的方法,于是就多了一只\(\log\)。其实没有必......
  • P4559 [JSOI2018] 列队 题解
    题目链接:列队半年前mark的题,结果现在一下子就会做了。顺便写写我的手玩过程和复杂度说明。考虑比较特殊的情况:比较特殊的,发现从黑色到红色区间我们无论咋选择,由于\(\left|a_{right}-a_{left}\right|\),这玩意如果\(right\)表示红色的一边,那么这个绝对值可以直接拆掉,那么......
  • [ARC171E] Rookhopper's Tour 题解
    题目链接首先把\(m=2\)和\(m\)为奇数的情况判掉,由于我们要对合法的摆放方案计数,而一个摆放方案要判断合法性就必须通过一组合法的移动过程,对移动的状态进行记录以此转移和优化显然没啥前途,因此我们考虑摆放方案和移动过程之间的联系。一个比较显然的观察是摆放方案和移动过......