CF650C Table Compression

CF650C Table Compression

给一个 (n imes m) 的非负整数矩阵 (a),让你求一个 (n imes m) 的非负整数矩阵 (b),满足以下条件

  1. (a_{i,j}<a_{i,k}),则 (b_{i,j}<b_{i,k})
  2. (a_{i,j}=a_{i,k}),则 (b_{i,j}=b_{i,k})
  3. (a_{i,j}<a_{k,j}),则 (b_{i,j}<b_{k,j})
  4. (a_{i,j}=a_{k,j}),则 (b_{i,j}=b_{k,j})
  5. (b) 中的最大值最小

(n imes mleq 10^6)

建图+并查集


先考虑 (a) 中没有重复元素的情况

发现,我们只需要对于每行每列,按值域从小到大,相邻两位置连边,然后 (b) 每个位置的权值即为到最小数的距离,在 DAG 上遍历一遍即可

但是若 (a) 中有重复元素,直接建图就没有正确性了

(trick) :对于同一行同一列的重复元素,建立并查集,进行操作时只用对根节点进行操作

时间复杂度 (O(nmlog nm))

代码

#include <bits/stdc++.h>
using namespace std;

#define get(x, y) ((x - 1) * m + y)
typedef pair <int, int> pii;
const int maxn = 1e6 + 10;
int n, m, tot, a[maxn], f[maxn], par[maxn];
struct node {
  int x, y;
  bool operator < (const node& o) const {
    return a[get(x, y)] < a[get(o.x, o.y)];
  }
} dat[maxn];
vector <int> g[maxn];

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

void unite(int x, int y) {
  par[find(x)] = find(y);
}

int dfs(int u) {
  if (~f[u]) return f[u]; f[u] = 0;
  for (int v : g[u]) f[u] = max(f[u], dfs(v));
  return ++f[u];
}

int main() {
  scanf("%d %d", &n, &m), tot = n * m;
  for (int i = 1; i <= tot; i++) {
    scanf("%d", a + i), par[i] = i;
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      dat[j] = node{i, j};
    }
    sort(dat + 1, dat + m + 1);
    for (int j = 1; j < m; j++) {
      int u = get(dat[j].x, dat[j].y);
      int v = get(dat[j + 1].x, dat[j + 1].y);
      if (a[u] == a[v]) unite(u, v);
    }
  }
  for (int j = 1; j <= m; j++) {
    for (int i = 1; i <= n; i++) {
      dat[i] = node{i, j};
    }
    sort(dat + 1, dat + n + 1);
    for (int i = 1; i < n; i++) {
      int u = get(dat[i].x, dat[i].y);
      int v = get(dat[i + 1].x, dat[i + 1].y);
      if (a[u] == a[v]) unite(u, v);
    }
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      dat[j] = node{i, j};
    }
    sort(dat + 1, dat + m + 1);
    for (int j = 1; j < m; j++) {
      int u = get(dat[j].x, dat[j].y);
      int v = get(dat[j + 1].x, dat[j + 1].y);
      if ((u = find(u)) != (v = find(v))) g[v].push_back(u);
    }
  }
  for (int j = 1; j <= m; j++) {
    for (int i = 1; i <= n; i++) {
      dat[i] = node{i, j};
    }
    sort(dat + 1, dat + n + 1);
    for (int i = 1; i < n; i++) {
      int u = get(dat[i].x, dat[i].y);
      int v = get(dat[i + 1].x, dat[i + 1].y);
      if ((u = find(u)) != (v = find(v))) g[v].push_back(u);
    }
  }
  memset(f, -1, sizeof f);
  for (int i = 1; i <= tot; i++) {
    if (find(i) == i) dfs(i);
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      printf("%d ", f[find(get(i, j))]);
    }
    putchar(10);
  }
  return 0;
}

一种 (shortest) 的做法

对于每个元素,按值域从小到大考虑,通过已访问到的行列最大值更新答案

时间复杂度 (O(nmlog nm))

代码

#include <bits/stdc++.h>
using namespace std;

#define get(x, y) ((x - 1) * m + y)
const int maxn = 1e6 + 10;
int n, m, tot, a[maxn], ans[maxn], par[maxn], val[2][maxn];
struct node {
  int x, y;
  bool operator < (const node& o) const {
    return a[get(x, y)] < a[get(o.x, o.y)];
  }
} dat[maxn];

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

int main() {
  scanf("%d %d", &n, &m), tot = n * m;
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      int pos = get(i, j);
      scanf("%d", a + pos), dat[pos] = node{i, j}, par[pos] = pos;
    }
  }
  sort(dat + 1, dat + tot + 1);
  for (int i = 1; i <= tot; i++) {
    int tx = dat[i].x, ty = dat[i].y, pos = get(tx, ty);
    int px = find(val[0][tx]), py = find(val[1][ty]), p = find(pos);
    ans[p] = max(ans[px] + (a[p] > a[px]), ans[py] + (a[p] > a[py]));
    if (a[p] == a[px]) par[px] = p;
    if (a[p] == a[py]) par[py] = p;
    val[0][tx] = val[1][ty] = p;
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= m; j++) {
      printf("%d ", ans[find(get(i, j))]);
    }
    putchar(10);
  }
  return 0;
}
原文地址:https://www.cnblogs.com/Juanzhang/p/10424880.html