2.2 图论建模 & 图论杂项

\({\Large 约定}\)

  1. 用集合符号表示位运算符号,用 $ \oplus $ 表示异或,特别的,$ i \in S$ 表示二进制数 \(S\) 的第 \(i\) 位为 \(1\)
  2. \(V\) 表示值域,\(\sum\) 表示字符集,\(\omega\) 表示 bitset 的常数 \((\omega = 64)\)
  3. 除去用 \(()/[]\) 表示开闭区间外,\([]\) 仅表示艾弗森约定,\(\{\}\) 仅表示集合,括号嵌套全用 \(()\)
  4. 字符串或序列角标为区间表示对应区间的子串

problems

\(\textcolor{white}{\mathrm{pw:123456}}\)

#625. 【统一省选2021 A卷】矩阵游戏

\(\textcolor{purple}{\mathrm{省选/NOI−}}\) tag 图论 差分约束 构造

先不考虑对 \(a_{i,j}\) 大小的限制,那么有一种简单构造:

  • \(a_{i,0}=0,a_{0,i}=0\)
  • \(a_{i,j}=b_{i-1,j-1}-a_{i-1,j}-a_{i,j-1}-a_{i-1,j-1}\)

这个的正确性是显然的。但是不一定满足 \(a_{i,j}\) 的范围限制。考虑如何调整。
对于一个 \(x\),如果对一行的所有数字一次进行 $+x,-x,+x,-x\cdotp $ 的操作,那么 \(b\) 矩阵不会发生变化。对列操作也是同理。设 \(r_i\) 为在第 \(i\) 行上操作的 \(x\) 值,设 \(c_i\) 为在第 \(i\) 列上操作的 \(x\) 值。
那么现在问题就转化成了对 \(r_i\)\(c_i\) 的限制了。根据 \(a_i\) 的限制可以列出下面的不等式:

\[0\leq a_{i,j}\pm c_i\pm d_j\leq 10^6 \]

在这里,\(a_{i,j}\) 都是确定的,那么将 \(a_{i,j}\) 移项得到:

\[-a_{i,j}\leq \pm c_i\pm d_j\leq 10^6-a_{i,j} \]

发现这个比较像差分约束的形式,考虑要怎么做。
那么对于 \(c_i\)\(d_j\),我们可以分别列出一个矩阵表示对应位置的 \(c_i\)\(d_j\) 的正负性。

\[行 \begin{bmatrix} + & - & + & - \\ + & - & + & - \\ + & - & + & - \\ + & - & + & - \\ \end{bmatrix} \\ \\ 列 \begin{bmatrix} + & + & + & + \\ - & - & - & - \\ + & + & + & + \\ - & - & - & - \\ \end{bmatrix} \]

但是这样可能会出现 \(c_i\)\(d_j\) 正负性相同的情况,因此可以将 \(c_i\)\(d_j\) 稍微位移一下:

\[行 \begin{bmatrix} + & - & + & - \\ - & + & - & + \\ + & - & + & - \\ - & + & - & + \\ \end{bmatrix} \\ \\ 列 \begin{bmatrix} - & + & - & + \\ + & - & + & - \\ - & + & - & + \\ + & - & + & - \\ \end{bmatrix} \]

这样每个位置 \(c_i\)\(d_j\) 的正负性就不一样了,也就可以转化成差分约束去做了。

\(c_i\)\(+\)\(d_j\)\(-\) 为例,上面的式子就变成了:

\[-a_{i,j}\leq c_i-d_j\leq 10^6-a_{i,j} \\ d_j\leq c_i+a_{i,j},c_i\leq d_j+10^6-a_{i,j} \]

那么只需要建 \((d_j,c_i,10^6-a_{i,j}),(c_i,d_j,a_{i,j})\) 这样两条边即可。

当然还需要建一个虚拟源点向每个点连一条长度为 \(0\) 的有向边。

正确性证明可以看下面的文章。

用 vector 存图会更快。

Uoj 上要限制一下判断负环的次数才能过,不知道为啥。

Code
  const int N = 1e6 + 10;
  const int M = 1e3 + 10;
  const int inf = 1e6;
  int n, m;
  int a[M][M], b[M][M];
  ll dis[N];
  bool vis[N];
  int cnt[N];
  std::vector<pii> ve[N];
  int q[N], he, tl;
  void init(int x) {
    REP(i, 0, x + 1)  ve[i].clear();
    return;
  }
  bool spfa() {
    he = 1, tl = 0;
    q[++tl] = 0;
    REP(i, 0, n + m + 2)  dis[i] = 1e9, cnt[i] = vis[i] = 0;
    dis[0] = 0;
    vis[0] = 1;
    while(he <= tl) {
      int u = q[he];
      he++;
      vis[u] = 0;
      cnt[u]++;
      if (cnt[u] == n + m + 1 or cnt[u] == 30)  return 0;//不知道什么神秘减枝
      for(auto it:ve[u]) {
        int v = it.fi, w = it.se;
        if (dis[v] > dis[u] + w) {
          dis[v] = dis[u] + w;
          if (!vis[v]) {
            q[++tl] = v;
            vis[v] = 1;
          }
        }
      }
    }
    return 1;
  }
  void main() {
    read(n, m);
    REP(i, 1, n - 1)
      REP(j, 1, m - 1)  read(b[i][j]);
    init(n + m + 2);
    REP(i, 2, n) {
      REP(j, 2, m) {
        a[i][j] = b[i - 1][j - 1] - a[i][j - 1] - a[i - 1][j] - a[i - 1][j - 1];
      }
    }
    REP(i, 1, n) { 
      REP(j, 1, m) {
        int u = i, v = j + n;
        if ((i + j) & 1)  std::swap(u, v);
        ve[v].pb({u, inf - a[i][j]});
        ve[u].pb({v, a[i][j]});
      }
    }
    REP(i, 1, n + m)  ve[0].pb({i, 0});
    if (!spfa())  return puts("NO"), void();
    puts("YES");
    REP(i, 1, n) {
      REP(j, 1, m) {
        int sum = (dis[i] - dis[j + n]);
        if ((i + j) & 1)  sum *= -1;
        a[i][j] += sum;
        std::cout << a[i][j] << ' ';
      }
      puts("");
    }
    return ;
  }

参考文献1

参考文献2

submission1

submission2

2026.01.30

posted @ 2026-01-30 10:13  yqfff_qwq  阅读(3)  评论(0)    收藏  举报