首页 > 其他分享 >[CERC2016]机棚障碍 Hangar Hurdles

[CERC2016]机棚障碍 Hangar Hurdles

时间:2022-10-13 17:46:26浏览次数:47  
标签:dep Hurdles ch 机棚 int void ret fa CERC2016

Description

给定一个 \(n\times n\) 的网格图,其中部分格点有障碍物使得箱子不能置于其上。规定箱子是一个奇数边长的正方形,其坐标为其中心格点的坐标。箱子只能上下左右移动,每次询问从一个格点能移动到另一个格点的最大箱子。

Solution

首先对于每个点 \((x,y)\) 用二分求出以这个点为中心格点的最大边长 \(d[x][y]\),是 \(O(n^2\log n)\) 的。

然后发现这是多组询问,所以不能暴力。


先把这个看成一个普通的无向图,把相邻的格点连无向边,边权为两个点的 \(d\) 值取较小值,那么相当于就是询问图上两点的最大瓶颈路径。

这个东西可以用 Kruskal 先跑出最大生成树,然后最大瓶颈路径就是这两点的树上最短路径。


证明:

先看到 Kruskal 过程:将边权从大到小排序,然后枚举,如果加上当前边不会出现环,就加边。相当于就是能连边就连边。

假设 \(u\) 和 \(v\) 所在集合在枚举到从大到小第 \(k\) 条边时第一次被合并,如果 \(u\) 和 \(v\) 的最大瓶颈路径小于当前边权,那么这相当于在这条边之前 \(u\) 和 \(v\) 的集合已经被合并了,那么这与假设矛盾。所以 Kruskal 跑出来的最大生成树就是最大瓶颈生成树。


注意:这可能是个森林,所以要对于每棵树都跑一遍最大生成树。

建完最大生成树后,询问就转化为求树上两点路径的最小边权,直接倍增 LCA 即可,每次是 \(O(\log n)\) 的。

于是整个程序复杂度就为 \(O(n^2\log n)\),由于时限开的很大,所以可以轻松通过。

Code

代码
#include <bits/stdc++.h>

#ifdef ORZXKR
#include <debug.h>
#else
#define debug(...) 1
#endif

using namespace std;

namespace FASTIO {
char ibuf[1 << 21], *p1 = ibuf, *p2 = ibuf;
char getc() {
  return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
template<class T> bool read(T &x) {
  x = 0; int f = 0; char ch = getc();
  while (ch < '0' || ch > '9') f |= ch == '-', ch = getc();
  while (ch >= '0' && ch <= '9') x = (x * 10) + (ch ^ 48), ch = getc();
  x = (f ? -x : x); return 1;
}
bool read(char &x) {
  while ((x = getc()) == ' ' || x == '\n' || x == '\r');
  return x != EOF;
}
bool read(char *x) {
  while ((*x = getc()) == '\n' || *x == ' ' || *x == '\r');
  if (*x == EOF) return 0;
  while (!(*x == '\n' || *x == ' ' || *x == '\r' || *x == EOF)) *(++x) = getc();
  *x = 0;
  return 1;
}
template<typename A, typename ...B> bool read(A &x, B &...y) { return read(x) && read(y...); }

char obuf[1 << 21], *o1 = obuf, *o2 = obuf + (1 << 21) - 1;
void flush() { fwrite(obuf, 1, o1 - obuf, stdout), o1 = obuf; }
void putc(char x) { *o1++ = x; if (o1 == o2) flush(); }
template<class T> void write(T x) {
  if (!x) putc('0');
  if (x < 0) x = -x, putc('-');
  char c[40]; int tot = 0;
  while (x) c[++tot] = x % 10, x /= 10;
  for (int i = tot; i; --i) putc(c[i] + '0');
}
void write(char x) { putc(x); }
template<typename A, typename ...B> void write(A x, B ...y) { write(x), write(y...); }
struct Flusher {
  ~Flusher() { flush(); }
} flusher;
} // namespace FASTIO
using FASTIO::read; using FASTIO::putc; using FASTIO::write;

const int kMaxN = 1e3 + 5, kMaxS = 1e6 + 5, dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

class DSU {
  public:
    void init(int n) {
      for (int i = 1; i <= n; ++i) {
        fa[i] = i;
      }
    }
    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;
    }
  private:
    int fa[kMaxS];
} d;

int n, q, yxy;
int dis[kMaxS];
char s[kMaxN][kMaxN];
vector<pair<int, int>> G[kMaxS];

int getid(int x, int y) {
  return (x - 1) * n + y;
}

void addE(int u, int v, int w) {
  G[u].emplace_back(v, w);
}

namespace GETDIS {

int sum[kMaxN][kMaxN], d[kMaxN][kMaxN];
queue<pair<int, int>> q;

int get(int m1, int n1, int m2, int n2) {
  return sum[m2][n2] - sum[m1 - 1][n2] - sum[m2][n1 - 1] + sum[m1 - 1][n1 - 1];
}

void solve() {
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + (s[i][j] == '#');
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      if (s[i][j] == '#') continue ;
      int L = 0, R = min({i - 1, n - i, j - 1, n - j}) + 1, res = 0;
      while (L + 1 < R) {
        int mid = (L + R) >> 1;
        if (!get(i - mid, j - mid, i + mid, j + mid)) L = res = mid;
        else R = mid;
      }
      d[i][j] = dis[getid(i, j)] = 2 * res + 1;
    }
  }
}

} // namespace GETDIS

namespace KRUSKAL {

struct Edge {
  int u, v, w;
} ed[kMaxS << 2];

DSU dd;

int tot, cnt[kMaxS], tt[kMaxS], fa[kMaxS];

void build() {
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      if (s[i][j] == '#') continue ;
      for (int k = 0; k < 2; ++k) {
        int tx = i + dx[k], ty = j + dy[k];
        if (tx < 1 || tx > n || ty < 1 || ty > n || s[tx][ty] == '#') continue ;
        int u = getid(i, j), v = getid(tx, ty), w = min(dis[u], dis[v]);
        ed[++tot] = {u, v, w}, d.unionn(u, v);
      }
    }
  }
}

void solve() {
  d.init(n * n), dd.init(n * n), build();
  for (int i = 1; i <= n * n; ++i) {
    if (dis[i]) ++tt[fa[i] = d.find(i)];
  }
  sort(ed + 1, ed + 1 + tot, [&] (Edge e1, Edge e2) { return e1.w > e2.w; });
  for (int i = 1; i <= tot; ++i) {
    int fu = dd.find(ed[i].u), fv = dd.find(ed[i].v);
    if (fu != fv) {
      if (cnt[fa[ed[i].u]] == tt[fa[ed[i].u]] - 1) continue ;
      dd.unionn(ed[i].u, ed[i].v);
      addE(ed[i].u, ed[i].v, ed[i].w), addE(ed[i].v, ed[i].u, ed[i].w);
      ++cnt[fa[ed[i].u]];
    }
  }
}

} // namespace KRUSKAL

namespace LCA {

int lg[kMaxS], dep[kMaxS], mi[kMaxS][22], fa[kMaxS][22];

void getlg() {
  memset(mi, 0x3f, sizeof(mi));
  lg[0] = -1;
  for (int i = 1; i <= n * n; ++i) {
    lg[i] = lg[i >> 1] + 1;
  }
}

void dfs(int u, int fat, int lw) {
  dep[u] = dep[fat] + 1, fa[u][0] = fat, mi[u][0] = lw;
  for (int i = 1; i <= lg[n * n]; ++i) {
    fa[u][i] = fa[fa[u][i - 1]][i - 1];
    mi[u][i] = min(mi[u][i - 1], mi[fa[u][i - 1]][i - 1]);
  }
  for (auto [v, w] : G[u]) {
    if (v == fat) continue ;
    dfs(v, u, w);
  }
}

int LCA(int x, int y) {
  if (dep[x] < dep[y]) swap(x, y);
  for (int i = lg[n * n]; ~i; --i) {
    if (dep[fa[x][i]] >= dep[y]) {
      x = fa[x][i];
    }
  }
  if (x == y) return x;
  for (int i = lg[n * n]; ~i; --i) {
    if (fa[x][i] != fa[y][i]) {
      x = fa[x][i], y = fa[y][i];
    }
  }
  return fa[x][0];
}

int getmx(int x, int y) {
  int lca = LCA(x, y), ret = 1e9;
  for (int i = lg[n * n]; ~i; --i) {
    if (dep[fa[x][i]] > dep[lca]) ret = min(ret, mi[x][i]), x = fa[x][i];
    if (dep[fa[y][i]] > dep[lca]) ret = min(ret, mi[y][i]), y = fa[y][i];
  }
  if (x != lca) ret = min(ret, mi[x][0]);
  if (y != lca) ret = min(ret, mi[y][0]);
  return ret;
}

} // namespace LCA

int main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  read(n);
  for (int i = 1; i <= n; ++i) {
    read(s[i] + 1);
  }
  GETDIS::solve(), KRUSKAL::solve(), LCA::getlg();
  for (int i = 1; i <= n * n; ++i) {
    if (dis[i] && d.find(i) == i) {
      LCA::dfs(i, 0, 1e9);
    }
  }
  read(q);
  while (q--) {
    int s1, t1, s2, t2;
    read(s1, t1, s2, t2);
    int u = getid(s1, t1), v = getid(s2, t2);
    if (d.find(u) == d.find(v)) write(LCA::getmx(getid(s1, t1), getid(s2, t2)), '\n');
    else write(0, '\n');
  }
  return 0;
}

标签:dep,Hurdles,ch,机棚,int,void,ret,fa,CERC2016
From: https://www.cnblogs.com/Soba/p/16788943.html

相关文章