P7515 [省選聯考 2021 A 卷] 矩陣遊戲 題解

下蛋爷發表於2024-08-24

Description

Alice 有一個 \(n \times m\) 的矩陣 \(a_{i, j}\)\(1 \le i \le n\)\(1 \le j \le m\)),其每個元素為大小不超過 \({10}^6\) 的非負整數。

Bob 根據該矩陣生成了一個 \((n - 1) \times (m - 1)\) 的矩陣 \(b_{i, j}\)\(1 \le i \le n - 1\)\(1 \le j \le m - 1\)),每個元素的生成公式為

\[b_{i, j} = a_{i, j} + a_{i, j + 1} + a_{i + 1, j} + a_{i + 1, j + 1} \]

現在 Alice 忘記了矩陣 \(a_{i, j}\),請你根據 Bob 給出的矩陣 \(b_{i, j}\) 還原出 \(a_{i, j}\)

\(2 \le n, m \le 300\)\(0 \le b_{i, j} \le 4 \times {10}^6\)

Solution

首先有個顯然的事實是隻要確定了第一行和第一列的值就能確定整個矩陣。

那麼可以先在第一行和第一列隨便填數,這時可能會有些數不在限制裡面,考慮調整。

注意到讓一行或一列進行類似 \(-x,+x,-x,+x\ldots-x,+x\) 的操作後仍然滿足 \(b\) 矩陣的性質,不妨設 \(x\) 為偏移量。

考慮只做這類操作,設 \(x_i\) 為第 \(i\) 行的偏移量,\(y_i\) 為第 \(i\) 列的偏移量。

那麼對於每個 \((i,j)\),一定滿足 \(0\leq a_{i,j}+(-1)^jx_i+(-1)^iy_j\leq 10^6\)。可以設 \(b_i=(-1)^ix_i,c_i=(-1)^{i+1}y_i\),那麼就是 \(0\leq (-1)^{i+j}b_i-(-1)^{i+j}c_j\leq 10^6\),這是個差分約束的形式,可以 spfa 求解。無解就等價於出現了負環。

下面證明一下透過上面所述的操作一定可以操作出任意符合條件的矩形:

由於只要確定了第一行和第一列的值就可以確定整個矩陣,所以可以先把行操作做完再做列操作,容易發現這樣第一行第一列的數可以取到任意值,也就是說這樣做可以構造出任意一個符合條件的矩陣。

時間複雜度:\(O(n^3)\)

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 305;

int n, m;
int a[kMaxN][kMaxN], b[kMaxN][kMaxN], dis[kMaxN * 2], cnt[kMaxN * 2];
bool inq[kMaxN * 2];
std::vector<std::pair<int, int>> G[kMaxN * 2];

bool spfa() {
  std::queue<int> q;
  for (int i = 1; i <= n + m; ++i)
    dis[i] = 1e9, cnt[i] = inq[i] = 0;
  q.emplace(1), dis[1] = 0, inq[1] = 1;
  for (; !q.empty();) {
    int u = q.front(); q.pop();
    inq[u] = 0;
    for (auto [v, w] : G[u]) {
      if (dis[v] > dis[u] + w) {
        dis[v] = dis[u] + w;
        if (!inq[v]) q.emplace(v), inq[v] = 1;
        if (++cnt[v] == n + m) return 0;
      }
    }
  }
  return 1;
}

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = 1; i <= n + m; ++i) G[i].clear();
  for (int i = 1; i < n; ++i)
    for (int j = 1; j < m; ++j)
      std::cin >> b[i][j];
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      if (i == 1 || j == 1) a[i][j] = 0;
      else a[i][j] = b[i - 1][j - 1] - a[i - 1][j - 1] - a[i - 1][j] - a[i][j - 1];
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      int L = -a[i][j], R = 1e6 - a[i][j];
      if ((i + j) & 1) std::swap(L, R), L = -L, R = -R;
      G[i].emplace_back(j + n, -L), G[j + n].emplace_back(i, R);
    }
  }
  if (!spfa()) return void(std::cout << "NO\n");
  std::cout << "YES\n";
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      int op = ((i + j) % 2 ? -1 : 1);
      std::cout << a[i][j] + dis[i] * op - dis[j + n] * op << ' ';
    }
    std::cout << '\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;
}

相關文章