欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CF650C Table Compression

程序员文章站 2022-03-12 20:06:22
...

CF650C Table Compression

给一个 \(n\times m\) 的非负整数矩阵 \(a\),让你求一个 \(n\times 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\times m\leq 10^6\)

建图+并查集


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

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

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

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

时间复杂度 \(O(nm\log 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(nm\log 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;
}